利用sscanf()函数得到屏幕的像素大小

在移植SDL,和把仙剑移植到openwrt上时,为了使游戏进入全屏模式,需要查询下液晶屏的像素大小。fbset指令可以查询得到。

fbset                                                     
                                                                                
mode "800x480-0"                                                                
        # D: 0.032 MHz, H: 0.034 kHz, V: 0.065 Hz                               
        geometry 800 480 800 960 32                                             
        timings 31599750 80 36 22 15 10 8                                       
        accel false                                                             
        rgba 8/16,8/8,8/0,8/24                                                  
endmode

通常来说,framebuffer的mode信息是这样的:

       mode
              geometry <xres> <yres> <vxres> <vyres> <depth>
              timings <pixclock> <left> <right> <upper> <lower> <hslen> <vslen>
              options <value>
              rgba <red,green,blue,alpha>
       endmode

通过cat /sys/下的系统信息,也可以的到,但是一下两个有区别:

cat /sys/class/graphics/fb0/virtual_size
800,960
对比

cat /sys/class/graphics/fb0/modes
U:800x480p-0

所以就可以在C语言中,利用fopen打开/sys/class/graphics/fb0/modes,再利用sscanf()函数可以得到屏幕的像素大小,全部代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void getFBSize(int *w, int *h) 
{
    char buf[256]={0};
    *w = 480;
    *h = 272;
    FILE *file=fopen("/sys/class/graphics/fb0/modes","r");
    if (!file) {
        return ;
    }   
    int tmp1=0,tmp2=0;
    if (fgets(buf, sizeof(buf), file)) {
        printf("read:%s\n",buf);
        if (sscanf(buf, "%*[^0-9]%dx%d", &tmp1, &tmp2) == 2) {
            *w = tmp1;
            *h = tmp2;
        }   
    }   
    fclose(file);
    return ;
}

int main(void)
{
        int wScreenWidth=0,wScreenHeight=0;
        getFBSize(&wScreenWidth,&wScreenHeight);
        printf("wScreenWidth=%d\nwScreenHeight=%d\n", wScreenWidth, wScreenHeight);
        return 0;
}


下面是摘录sscanf的使用简介,非常有意思!

http://baike.baidu.com/view/1364018.htm


sscanf

  名称:

  sscanf()-从一个字符串中读进与指定格式相符的数据.

  函数原型:

  Intsscanf( string str, string fmt, mixed var1, mixed var2 ... );

  intscanf( const char *format [,argument]... );

  说明:

  sscanfscanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。

  其中的format可以是一个或多个{%[*][width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | %符号}

  注:

  1*亦可用于格式中,(%*d%*s)加了星号(*)表示跳过此数据不读入.(也就是不把此数据读入参数中)

  2{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d

  3width表示读取宽度。

  4{h| l | I64 | L}:参数的size,通常h表示单字节sizeI表示2字节size,L表示4字节size(double例外),l64表示8字节size

  5type:这就很多了,就是%s,%d之类。

  6、特别的:%*[width][{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值

  支持集合操作:

  %[a-z]表示匹配az中任意字符,贪婪性(尽可能多的匹配)

  %[aB']匹配aB'中一员,贪婪性

  %[^a]匹配非a的任意字符,贪婪性

[编辑本段]

例子:

  1.常见用法。

  charbuf[512] = ;

  sscanf("123456", "%s", buf);

  printf("%c\n",buf);

  结果为:123456

  2.取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。

  sscanf("123456", "%4s", buf);

  printf("%s\n",buf);

  结果为:1234

  3.取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。

  sscanf("123456abcdedf", "%[^ ]", buf);

  printf("%s\n",buf);

  结果为:123456

  4.取仅包含指定字符集的字符串。如在下例中,取仅包含19和小写字母的字符串。

  sscanf("123456abcdedfBCDEF","%[1-9a-z]", buf);

  printf("%s\n",buf);

  结果为:123456abcdedf

  5.取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。

  sscanf("123456abcdedfBCDEF","%[^A-Z]", buf);

  printf("%s\n",buf);

  结果为:123456abcdedf

  6、给定一个字符串iios/12DDWDFF@122,获取/@之间的字符串,先将"iios/"过滤掉,再将非'@'的一串内容送到buf

  sscanf("iios/12DDWDFF@122","%*[^/]/%[^@]", buf);

  printf("%s\n",buf);

  结果为:12DDWDFF

  7、给定一个字符串““hello,world”,仅保留world。(注意:“,”之后有一空格)

  sscanf(“hello,world”, "%*s%s", buf);

  printf("%s\n",buf);

  结果为:world

  %*s表示第一个匹配到的%s被过滤掉,即hello被过滤了

  如果没有空格则结果为NULL

  sscanf的功能很类似于正则表达式,但却没有正则表达式强大,所以如果对于比较复杂的字符串处理,建议使用正则表达式.

  //-------------------------------------------------------

  sscanf,表示从字符串中格式化输入

  上面表示从str中,输入数字给x,就是32700

  久以前,我以为c没有自己的splitstring函数,后来我发现了sscanf;一直以来,我以为sscanf只能以空格来界定字符串,现在我发现我错了。

  sscanf是一个运行时函数,原形很简单:

  intsscanf(

  constchar *buffer,

  constchar *format [,

  argument] ...

  );

  它强大的功能体现在对format的支持上。

  我以前用它来分隔类似这样的字符串2006:03:18:

  inta, b, c;

  sscanf("2006:03:18","%d:%d:%d", a, b, c);

  以及2006:03:18- 2006:04:18:

  charsztime1[16] = "", sztime2[16] = "";

  sscanf("2006:03:18- 2006:04:18", "%s - %s", sztime1, sztime2);

  但是后来,我需要处理2006:03:18-2006:04:18

  仅仅是取消了‘-’两边的空格,却打破了%s对字符串的界定。

  我需要重新设计一个函数来处理这样的情况?这并不复杂,但是,为了使所有的代码都有统一的风格,我需要改动很多地方,把已有的sscanf替换成我自己的分割函数。我以为我肯定需要这样做,并伴随着对sscanf的强烈不满而入睡;一觉醒来,发现其实不必。

  format-type中有%[]这样的typefield。如果读取的字符串,不是以空格来分隔的话,就可以使用%[]

  %[]类似于一个正则表达式。[a-z]表示读取a-z的所有字符,[^a-z]表示读取除a-z以外的所有字符。

  所以那个问题也就迎刃而解了:

  sscanf("2006:03:18- 2006:04:18", "%[0-9,:] - %[0-9,:]", sztime1,sztime2);

  在softmse(Jake)的问题贴http://community.csdn.net/Expert/topic/4843/4843294.xml?temp=.4321558中,周星星给出了一个很coolsscanf用例,而后通过学习,发现sscanf真棒,现做一总结。

  原问题:

  iios/12DDWDFF@122

  获取/@之间的字符串怎么做

  C程序里面有什么函数吗?

  周星星的代码:

  #include<stdio.h>

  intmain()

  {

  constchar* s = "iios/12DDWDFF@122";

  charbuf[20];

  sscanf(s, "%*[^/]/%[^@]", buf );

  printf("%s\n", buf );

  return0;

  }

  结果为:12DDWDFF

  sscanfscanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。

  函数原型:

  intscanf( const char *format [,argument]... );

  其中的format可以是一个或多个{%[*][width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | %符号}

  注:{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d

  width:宽度,一般可以忽略,用法如:

  constchar sourceStr[] = "hello, world";

  charbuf[10] = ;

  sscanf(sourceStr,"%5s", buf); //%5s,只取5个字符

  cout<< buf<< endl;

  结果为:hello

  {h| l | I64 | L}:参数的size,通常h表示单字节sizeI表示2字节size,L表示4字节size(double例外),l64表示8字节size

  type:这就很多了,就是%s,%d之类。

  特别的:

  %*[width][{h | l | I64 | L}]type表示满足该条件的被过滤掉,不会向目标参数中写入值。如:

  constchar sourceStr[] = "hello, world";

  charbuf[10] = ;

  sscanf(sourceStr,"%*s%s", buf); //%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了

  cout<< buf<< endl;

  结果为:world

  支持集合操作:

  %[a-z]表示匹配az中任意字符,贪婪性(尽可能多的匹配)

  %[aB']匹配aB'中一员,贪婪性

  %[^a]匹配非a的任意字符,贪婪性

  是不是感觉眼熟了啊,不错,这和正则表达式很相似,而且仍然支持过滤,即可以有%*[a-z].如:

  星星大哥例子回顾:

  constchar* s = "iios/12DDWDFF@122";

  charbuf[20];

  sscanf(s, "%*[^/]/%[^@]", buf );

  printf("%s\n", buf );

  由例子3-》取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。

  sscanf("123456abcdedf", "%[^ ]", buf);

  printf("%s\n",buf);

  结果为:123456

  所以周星星的代码总结应该为:

  constchar* s = "iios/12DDWDFF@122";

  charbuf[20];

  sscanf(s, "%*[^/]/%[^@]", buf );

  printf("%s\n", buf );

  先将"iios/"过滤掉,再将到字符'@'为止的一串12DDWDFF(由例3可得此串到@为止,把@122舍掉)内容即是:12DDWDFF送到buf中,得到结果。

  PS:向星星大哥和softmse(Jake)致谢了,有了你们才有它啊





sscanf()什么意思

悬赏分:0-解决时间:2007-10-1221:05

#include<stdio.h>

#include<stdlib.h>


voidmain()

{

charstr[]="32700";

int x;

sscanf(&str[0],"%d",&x);

printf("%d",x-'0');

}

提问者:xiuye1987-千总四级

最佳答案

sscanf,表示从字符串中格式化输入


上面表示从str中,输入数字给x,就是32700

久以前,我以为c没有自己的splitstring函数,后来我发现了sscanf;一直以来,我以为sscanf只能以空格来界定字符串,现在我发现我错了。

sscanf是一个运行时函数,原形很简单:

intsscanf(

constchar *buffer,

constchar *format [,

argument] ...

);

它强大的功能体现在对format的支持上。

我以前用它来分隔类似这样的字符串2006:03:18:

int a,b, c;

sscanf("2006:03:18","%d:%d:%d", a, b, c);

以及2006:03:18- 2006:04:18:

charsztime1[16] = "", sztime2[16] = "";

sscanf("2006:03:18- 2006:04:18", "%s - %s", sztime1, sztime2);

但是后来,我需要处理2006:03:18-2006:04:18

仅仅是取消了‘-’两边的空格,却打破了%s对字符串的界定。

我需要重新设计一个函数来处理这样的情况?这并不复杂,但是,为了使所有的代码都有统一的风格,我需要改动很多地方,把已有的sscanf替换成我自己的分割函数。我以为我肯定需要这样做,并伴随着对sscanf的强烈不满而入睡;一觉醒来,发现其实不必。

format-type中有%[]这样的typefield。如果读取的字符串,不是以空格来分隔的话,就可以使用%[]

%[]类似于一个正则表达式。[a-z]表示读取a-z的所有字符,[^a-z]表示读取除a-z以外的所有字符。

所以那个问题也就迎刃而解了:

sscanf("2006:03:18- 2006:04:18", "%[0-9,:] - %[0-9,:]", sztime1,sztime2);

softmse(Jake)的问题贴http://community.csdn.net/Expert/topic/4843/4843294.xml?temp=.4321558中,周星星给出了一个很coolsscanf用例,而后通过学习,发现sscanf真棒,现做一总结。


原问题:


iios/12DDWDFF@122

获取/@之间的字符串怎么做

C程序里面有什么函数吗?


周星星的代码:


#include<stdio.h>


intmain()

{

constchar* s = "iios/12DDWDFF@122";

charbuf[20];


sscanf(s, "%*[^/]/%[^@]", buf );

printf("%s\n", buf );


return0;

}


结果为:12DDWDFF


sscanfscanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。


函数原型:


intscanf( const char *format [,argument]... );

其中的format可以是一个或多个{%[*][width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | %符号}


注:{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d



width:宽度,一般可以忽略,用法如:


const char sourceStr[] = "hello, world";


charbuf[10] = {0};


sscanf(sourceStr,"%5s", buf); //%5s,只取5个字符


cout<< buf<< endl;


结果为:hello


{h |l | I64 | L}:参数的size,通常h表示单字节sizeI表示2字节size,L表示4字节size(double例外),l64表示8字节size


type:这就很多了,就是%s,%d之类。



特别的:


%*[width][{h | l | I64 | L}]type表示满足该条件的被过滤掉,不会向目标参数中写入值。如:


const char sourceStr[] = "hello, world";


charbuf[10] = {0};


sscanf(sourceStr,"%*s%s", buf); //%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了


cout<< buf<< endl;


结果为:world


支持集合操作:


%[a-z]表示匹配az中任意字符,贪婪性(尽可能多的匹配)


%[aB']匹配aB'中一员,贪婪性


%[^a]匹配非a的任意字符,贪婪性


是不是感觉眼熟了啊,不错,这和正则表达式很相似,而且仍然支持过滤,即可以有%*[a-z].如:


星星大哥例子回顾:


constchar* s = "iios/12DDWDFF@122";

charbuf[20];


sscanf(s, "%*[^/]/%[^@]", buf );

printf("%s\n", buf );


先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中,cool.得到结果。


PS:向星星大哥和softmse(Jake)致谢了,有了你们才有它啊







sscanf()的用法和例子

2009-06-1216:03

这里有些sscanf()的一些使用说明,都是从论坛,Blog里整理出来的。供大家使用。
  
通过学习和使用个人认为,在字符串格式不是很复杂,但是也并不简单的时候用这个函数比较合适,这个尺度就要靠自己把握了,字符串不是很复杂,但自己写个处理的函数比较麻烦,效率也不高,就用这个函数,如果字符串很复杂,那就用正则表达式吧。
不多说了,看看下面这些介绍和列子吧!

名称:sscanf()-从一个字符串中读进与指定格式相符的数据.
函数原型:
Intsscanf( string str, string fmt, mixed var1, mixed var2 ... );
intscanf( const char *format [,argument]... );

说明:
sscanf
scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
其中的format可以是一个或多个{%[*][width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | %符号}

支持集合操作:
    %[a-z]
表示匹配az中任意字符,贪婪性(尽可能多的匹配)
    %[aB']
匹配aB'中一员,贪婪性
    %[^a]
匹配非a的任意字符,贪婪性

例子:
1.
常见用法。
   char buf[512] = {0};
    sscanf("123456 ","%s", buf);
    printf("%s\n",buf);
结果为:123456

2.取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。
   sscanf("123456 ", "%4s", buf);
   printf("%s\n", buf);
结果为:1234

3.取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。
   sscanf("123456 abcdedf", "%[^ ]", buf);
   printf("%s\n", buf);
结果为:123456

4.
取仅包含指定字符集的字符串。如在下例中,取仅包含19和小写字母的字符串。
   sscanf("123456abcdedfBCDEF", "%[1-9a-z]",buf);
    printf("%s\n",buf);
结果为:123456abcdedf

5.
取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。
   sscanf("123456abcdedfBCDEF", "%[^A-Z]",buf);
    printf("%s\n",buf);
结果为:123456abcdedf

6、给定一个字符串iios/12DDWDFF@122,获取/@之间的字符串,先将"iios/"过滤掉,再将非'@'的一串内容送到buf
   sscanf("iios/12DDWDFF@122","%*[^/]/%[^@]", buf);
   printf("%s\n", buf);
结果为:12DDWDFF

7
、给定一个字符串““hello,world”,仅保留world。(注意:“,”之后有一空格)

    sscanf(“hello,world”, "%*s%s", buf);  
   printf("%s\n",buf);
结果为:world
%*s
表示第一个匹配到的%s被过滤掉,即hello被过滤了
如果没有空格则结果为NULL
8

char*s="1try234delete5"
则:
sscanf(s,"1%[^2]234%[^5]", s1, s2);
scanf
format中出现的非转换字符(%之前或转换字符之后的字符),即此例中的1234用来跳过输入中的相应字符;
‘[]’
的含义与正则表达式中相同,表示匹配其中出现的字符序列;^表示相反。使用[]时接收输入的变量必须是有足够存储空间的charsignedcharunsignedchar数组。记住[也是转换字符,所以没有s了。

8、分割以某字符标记的字符串。

chartest[]="222,333,444,,,555,666";
chars1[4],s2[4],s3[4],s4[4],s5[4],s6[4],s7[4];
sscanf(test,"%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]",s1,s2,s3,s4,s5,s6,s7);
printf("sssa1=%s",s1);
printf("sssa2=%s",s2);
printf("sssa3=%s",s3);
printf("sssa4=%s",s4);
printf("sssa5=%s",s5);
printf("sssa6=%s",s6);
printf("sssa7=%s",s7);
9
、一个提取用户个人资料中邮件地址的例子
#include<cstdlib>
#include<cstdio>
usingnamespace std;
int main()
{
    chara[20]={0};
    char b[20]={0};
   //
假设email地址信息以';'结束
   sscanf("email:[email protected];","%*[^:]:%[^;]",a);
   //
假设email地址信息没有特定的结束标志
   sscanf("email:[email protected]","%*[^:]:%s",b);
   printf("%s\n",a);
   printf("%s\n",b);
   system("pause");
    return0;
}
关键是"%*[^:]:%[^;]""%*[^:]:%s"这两个参数的问题
%*[^:]   
表示满足"[]"里的条件将被过滤掉,不会向目标参数中写入值。这里的意思是在
           
第一个':'之前的字符会在写入时过滤掉,'^'是表示否定的意思,整个参数翻译
           
成白话就是:将在遇到第一个':'之前的(不为':'的)字符全部过滤掉。
:        
自然就是跳过':'的意思。
%[^;]    
拷贝字符直到遇到';'

一下摘自:http://blog.csdn.net/lbird/archive/2007/08/03/1724429.aspx
%[]
的用法:%[]表示要读入一个字符集合,如果[后面第一个字符是”^”,则表示反意思。

                    []内的字符串可以是1或更多字符组成。空字符集(%[])是违反规定的,可

                    导致不可预知的结果。%[^]也是违反规定的。
         

%[a-z]读取在a-z之间的字符串,如果不在此之前则停止,如

             chars[]="hello, my friend” ;        //注意:,逗号在不a-z之间

             sscanf(s, “%[a-z]”, string ) ; // string=hello


%[^a-z]
读取不在 a-z之间的字符串,如果碰到a-z之间的字符则停止,如

             chars[]="HELLOkitty” ;        //注意:,逗号在不a-z之间

             sscanf(s, “%[^a-z]”, string ) ; // string=HELLO


%*[^=]   
前面带 *号表示不保存变量。跳过符合条件的字符串。

             chars[]="notepad=1.0.0.1001" ;

      charszfilename [32] = "" ;

      inti = sscanf( s, "%*[^=]", szfilename ) ; //szfilename=NULL,因为没保存

inti = sscanf( s, "%*[^=]=%s", szfilename ) ; //szfilename=1.0.0.1001

%40c     读取40个字符

      Therun-time

librarydoes not automatically append a null terminator

tothe string, nor does reading 40 characters

automaticallyterminate the scanf() function. Because the

libraryuses buffered input, you must press the ENTER key

toterminate the string scan. If you press the ENTER before

thescanf() reads 40 characters, it is displayed normally,

andthe library continues to prompt for additional input

untilit reads 40 characters


%[^=]    
读取字符串直到碰到’=’号,’^’后面可以带更多字符,如:

             chars[]="notepad=1.0.0.1001" ;

      charszfilename [32] = "" ;

      inti = sscanf( s, "%[^=]", szfilename ) ; //szfilename=notepad     

      如果参数格式是:%[^=:],那么也可以从notepad:1.0.0.1001读取notepad

             

使用例子:

chars[]="notepad=1.0.0.1001" ;

charszname [32] = "" ;

charszver [32] = “” ;

sscanf(s, "%[^=]=%s", szname , szver ) ; // szname=notepad,szver=1.0.0.1001

总结:%[]有很大的功能,但是并不是很常用到,主要因为:

1、许多系统的scanf函数都有漏洞.(典型的就是TC在输入浮点型时有时会出错).

2、用法复杂,容易出错.

3、编译器作语法分析时会很困难,从而影响目标代码的质量和执行效率.

个人觉得第3点最致命,越复杂的功能往往执行效率越低下。而一些简单的字符串分析我们可以自已处理。





Byte变量存储为单精度型、无符号整型、8位(1个字节)的数值形式,范围在0255之间。

Byte数据类型在存储二进制数据时很有用。









函数名:atoi

功 能:把字符串转换成长整型数

用 法:int atoi(const char *nptr);

程序例:

#include<stdlib.h>

#include<stdio.h>


intmain(void)

{

intn;

char*str = "12345.67";


n =atoi(str);

printf("string= %s integer = %d\n", str, n);

return0;

}

窗体顶端

窗体底端



窗体顶端

标题<>>发布者<>>

窗体底端








480×60广告位虚位以待Email:[email protected]

120×60广告位







首  页

|

技术论坛

|

技术文章

|

程序源码

|

合作项目

|

在线教程

|

资料下载

|

读书笔记

|

发布文章

|

RSS

|

站点地图




当前位置:首页>>技术文章>>网络与通讯协议


文章标题:VC++6.0实现PC机与单片机之间串行通信的方法
原作 者:于小亿 王 辉 张志学
原出 处:不详
发布 者:leapwang
发布类型:原创
发布日期:2006-12-06
今日/总浏览:1/4935




摘要 详细介绍了在Windows环境下应用VC++实现PC机与单片机的几种串行通信方法,给出了用VisualC++6.0编写的PC机程序和用C51编写的单片机通信程序。经实际应用系统运行稳定可靠。

关键词VisualC++类串行通信


工业控制领域(如DCS系统),经常涉及到串行通信问题。为了实现微机和单片机之间的数据交换,人们用各种不同方法实现串行通信,如DOS下采用汇编语言或C语言,但在Windows环境下却存在一些困难和不足。在Windows操作系统已经占据统治地位的情况下(何况有些系统根本不支持DOSWindows2000)开发Windows环境下串行通信技术就显得日益重要。

VC++6.0
是微软公司于1998年推出的一种开发环境,以其强大的功能,友好的界面,32位面向对象的程序设计及ActiveX的灵活性而受广大软件开发者的青睐,被广泛应用于各个领域。应用VC++开发串行通信目前通常有如下几种方法:一是利用WindowsAPI通信函数;二是利用VC的标准通信函数_inp_inpw_inpd_outp_outpw_outpd等直接对串口进行操作;三是使用MicrosoftVisualC++的通信控件(MSComm);四是利用第三方编写的通信类。以上几种方法中第一种使用面较广,但由于比较复杂,专业化程度较高,使用较困难;第二种需要了解硬件电路结构原理;第三种方法看来较简单,只需要对串口进行简单配置,但是由于使用令人费解的VARIANT类,使用也不是很容易;第四种方法是利用一种用于串行通信的CSerial类(这种类是由第三方提供),只要理解这种类的几个成员函数,就能方便的使用。笔者利用CSerial类很方便地实现了在固定式EBM气溶胶灭火系统分区启动器(单片机系统)与上位机的通信。以下将结合实例,给出实现串行通信的几种方法。

1Windows API
通信函数方法

与通信有关的WindowsAPI函数共有26个,但主要有关的有:

CreateFile()
用“comn”n为串口号)作为文件名就可以打开串口。

ReadFile()
读串口。

WriteFile()
写串口。

CloseHandle()
关闭串口句柄。初始化时应注意CreateFile()函数中串口共享方式应设为0,串口为不可共享设备,其它与一般文件读写类似。以下给出API实现的源代码。

 

1.1
发送的例程

//
声明全局变量

HANDLEm_hIDComDev


OVERLAPPEDm_OverlappedRead, m_Over lappedWrite;

//
初始化串口

voidCSerialAPIView::OnInitialUpdate()

{

CView::OnInitialUpdate();

CharszComParams[50];

DCBdcb;

Memset(&m_OverlappedRead, 0, sizeof(OVERLAPPED));

Memset(&m_OverlappedWrite, 0,sizeof (OVERLAPPED));

m_hIDComDev =NULL;

m_hIDComDev = CreateFile(“COM2”,GENERIC_READ
GENERIC_WRITE,0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMALFILE_FLAG_OVERLAPPED,NULL);

if (m_hIDComDev ==NULL)

{

AfxMessageBox(“Can not openserial port!”);

gotoendd;

}

memset(&m_OverlappedRead, 0,sizeof (OVERLAPPED));

memset(&m_OverlappedWrite,0, sizeof (OVERLAPPED));

COMMTIMEOUTSCommTimeOuts;

CommTimeOuts.ReadIntervalTimeout=0×FFFFFFFF;

CommTimeOuts.ReadTotalTimeoutMultiplier = 0;

CommTimeOuts.ReadTotalTimeoutConstant = 0;

CommTimeOuts.WriteTotalTimeoutMultiplier = 0;

CommTimeOuts.WriteTotalTimeoutConstant =5000;

SetCommTimeouts(m_hIDComDev,&CommTimeOuts);

Wsprintf(szComparams,“COM2:9600, n, 8, 1”);

m_OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE,NULL);

m_OverlappedWrite. hEvent =CreateEvent(NULL, TRUE, FALSE, NULL);

dcb.DCBlength = sizeof(DCB);

GetCommState(m_hIDComDev,&dcb);

dcb. BaudRate = 9600;

dcb.ByteSize= 8;

unsigned char ucSet;

ucSet =(unsigned char) ((FC_RTSCTS&FC_DTRDSR) != 0);

ucSet= (unsigned char) ((FC_RTSCTS&FC_RTSCTS) ! =0);

ucSet = (unsigned char)((FC_RTSCTS&FC_XONXOFF) ! = 0);

if(!SetCommState(m_hIDComDev,&dcb)


!SetupComm(m_hIDComDev,10000,10000)


m_OverlappedRead.hEvent ==NULL


m_OverlappedWrite.hEvent ==NULL)

{

DWORD dwError =GetLastError();

if (m_OverlappedRead. hEvent !=NULL) CloseHandle(m_OverlappedRead. hEvent);

if(m_OverlappedWrite. hEvent != NULL)CloseHandle(m_OverlappedWrite.hEvent);

CloseHandle(m_hIDComDev);

}

endd:

;

}

//
发送数据

voidCSerialAPIView::OnSend()

{

charszMessage[20] = “thank you very much”;

DWORDdwBytesWritten;

for (int i=0; i
sizeof(szMessage);i++)

{

WriteFile(m_hIDComDev,(LPSTR)&szMessage[i], 1, &dwBytesWritten,&m_OverlappedWrite);

if(WaitForSingleObject(m_OverlapperWrite, hEvent,1000))dwBytesWritten =0;

else{

GentOverlappedResult(m_hIDComDev,&m_OverlappedWrite, &dwBytesWritten,FALSE);

m_OverlappedWrite. Offset +=dwBytesWritten;

}

dwBytesWritten++;

}

}

 

1.2
接收例程

DCBComDcb; //
设备控制块

HANDLEhCom; //global handle

hCom = CreateFile("COM1",GENERIC_READ|GENERIC_WRITE,0,

NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

if(hCom==INVALID_HANDLE_VALUE)

{

AfxMessageBox("
无法打开串行口");

}

else

{

COMMTIMEOUTSCommTimeOuts ;

SetCommMask(hCom, EV_RXCHAR );

SetupComm(hCom, 4096, 4096 ) ; /*
设置收发缓冲区尺寸为4K*/

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT |

PURGE_TXCLEAR| PURGE_RXCLEAR ) ;//
清收发缓冲区

//
以下初始化结构变量CommTimeOuts,设置超时参数CommTimeOuts.ReadIntervalTimeout= 0×FFFFFFFF ;

CommTimeOuts.ReadTotalTimeoutMultiplier= 0 ;

CommTimeOuts.ReadTotalTimeoutConstant = 4000;

CommTimeOuts.WriteTotalTimeoutMultiplier =0;

CommTimeOuts.WriteTotalTimeoutConstant = 4000;

SetCommTimeouts(hCom, &CommTimeOuts );//
设置超时参数

ComDcb.DCBlength= sizeof( DCB ) ;

GetCommState( hCom, &ComDcb) ; //
获取当前参数

ComDcb.BaudRate=9600; //
波特率

ComDcb.ByteSize= 8; //
数据位

ComDcb.Parity= 0; /*
校验0~4=no,odd, even, mark, space */

SetCommState(hCom,&ComDcb ) ;

} //
设置新的通信参数

接收可用定时器或线程等

DWORDdRead,dReadNum;

unsigned char buff[200];

dRead=ReadFile(hCom, buff, 100, &dReadNum,NULL); //
接收100个字符,

//dReadNum
为实际接收字节数

 

2
利用端口函数直接操作

这种方式主要是采用两个端口函数_inp(),_outp()实现对串口的读写,其中读端口函数的原型为:

int_inp(unsigned shotport)

该函数从端口读取一个字节,端口号为0~65535

写端口的函数原型为:

int_outp(unsigned shot port, intdatabyte)

该函数向指定端口写入一个字节。

不同的计算机串口地址可能不一样,通过向串口的控制及收发寄存器进行读写,可以实现灵活的串口通信功能,由于涉及具体的硬件电路讨论比较复杂,在此不加赘述。

 

3MSComm
控件

MSComm
控件是微软开发的专用通信控件,封装了串口的所有功能,使用很方便,但在实际应用中要小心对其属性进行配置。下面详细说明该类应用方法。

3.1MSComm
控件的属性

CommPort
:设置串口号,类型short:1-comm1 2-comm2.

Settings
:设置串口通信参数,类型CString:B波特率,P奇偶性(N无校验,E偶校验,O奇校验),D字节有效位数,S停止位。

PortOpen
:设置或返回串口状态,类型BOOLTURE打开,FALSE关闭。

InputMode:
设置从接收缓冲区读取数据的格式,类型long0-Text1-Bin

Input:
从接收缓冲区读取数据,类型VARIANT

InBufferCount:
接收缓冲区中的字节数,类型:short

InBufferSize:
接收缓冲区的大小,类型:short

Output:
向发送缓冲区写入数据,类型:VARIANT

OutBufferCount:
发送缓冲区中的字节数,类型:short

OutBufferSize:
发送缓冲区的大小,类型:short

InputLen:
设置或返回Input读出的字节数,类型:short

CommEvent:
串口事件,类型:short

 

3.2
程序示例

串口初始化

if(!m_comm.GetPortOpen())

m_comm.SetPortOpen(TURE);/*
打开串口*/

m_comm.SetSettings("4800,n,8,1");/*
串口参数设置*/

m_comm.SetInputMode(0);/*
设置TEXT缓冲区输入方式*/

m_comm.SetRthresHold(1);/*
每接收一个字符则激发OnComm()事件*/

接收数据

m_comm.SetInputLen(1);/*
每次读取一个字符

VARINATV1=m_comm.GetInput();

/*
读入字符*/

m_V1=V1.bstrval;

发送字符m_comm.SetOutput(Colevariant("Hello"); /*发送“Hello”*/

3.3
注意

SetOutput
方法可以传输文本数据或二进制数据。用SetOutput方法传输文本数据,必须定义一个包含一个字符串的Variant。发送二进制数据,必须传递一个包含字节数组的VariantOutput属性。正常情况下,如果发送一个ANSI字符串到应用程序,可以以文本数据的形式发送。如果发送包含嵌入控制字符、Null字符等的数据,要以二进制形式发送。此处望引起读者注意,笔者曾经在此犯错。

 

4VC++
CSerial

4.1
串行通信类CSerial简介

Cserial
是由MuMegaTechnologies公司提供的一个免费的VC++类,可方便地实现串行通信。以下为该类定义的说明部分。

classCSerial

{

public:

CSerial();

~CSerial();

BOOLOpen( int nPort = 2, int nBaud = 9600 );

BOOLClose( void );

int ReadData( void *, int );

intSendData( const char *, int );

intReadDataWaiting( void );

BOOL IsOpened( void ){return( m_bOpened ); }

protected:

BOOLWriteCommByte( unsigned char );

HANDLEm_hIDComDev;

OVERLAPPED m_OverlappedRead,m_OverlappedWrite;

BOOL m_bOpened;

}

 

4.2
串行通信类Cserial成员函数简介

1.CSerial
::Cserial是类构造函数,不带参数,负责初始化所有类成员变量。

2.CSerial::Open
这个成员函数打开通信端口。带两个参数,第一个是埠号,有效值是14,第二个参数是波特率,返回一个布尔量。

3.CSerial:: Close
函数关闭通信端口。类析构函数调用这个函数,所以可不用显式调用这个函数。

4.CSerial::SendData
函数把数据从一个缓冲区写到串行端口。它所带的第一个参数是缓冲区指针,其中包含要被发送的资料;这个函数返回已写到端口的实际字节数。

5.CSerial:: ReadDataWaiting
函数返回等待在通信端口缓冲区中的数据,不带参数。

6.CSerial::ReadData
函数从端口接收缓冲区读入数据。第一个参数是void*缓冲区指针,资料将被放入该缓冲区;第二个参数是个整数值,给出缓冲区的大小。

  

4.3
应用VC类的一个实例

1.
固定式EBM气溶胶灭火系统简介

固定式EBM气溶胶灭火装置分区启动器是专为EBM灭火装置设计的自动控制设备。可与两线制感温、感烟探测器配套使用,当监测部位发生火情时,探测器发出电信号给分区启动器,经逻辑判断后发出声、光报警,延时后自动启动EBM灭火装置。为了便于火灾事故的事后分析,需对重要的火警事件和关键性操作进行记录,记录应能从PC机读出来;PC机能控制、协调整个系统的工作,这些都涉及通信。本例中启动器采用RS-485通信接口,系统为主从式网络,PC机为上位机。具体的通信协议为:(1)下位机定时向上传送记录的事件;(2)应答发送,即PC机要得到最新事件记录,而传送时间未到时,PC机发送命令,下位机接收命令后,把最新记录传给上位机;(3)上位机发送其它命令如校时、启动、停止、手/自动等。

2.
通信程序设计

部分上位机程序

1)发送命令字程序,代码如下

voidCCommDlg::OnSend()

{

CSerialSerial;

//
构造串口类,初始化串行口

if(Serial.Open(2,9600))//if-1

//
打开串行口2,波特率为9600bps

{

staticchar szMessage[]="0";

//
命令码(可定义各种命令码)

intnBytesSent;

intcount=0;

resend:

nBytesSent=Serial.SendData(szMessage,strlen(szMessage));

//
发送命令码

charrdMessage [20];

if (Serial.ReadDataWaiting())//if-2

{

Serial.ReadData(rdMessage,88);

//rdMessage
定义接收字节存储区,为全局变量//

if((rdMessage[0]!=0x7f)&&(count<3))

{

count++;

gotoresend

}

if(count>=3)

MessageBox(“
发送命令字失败”);

}

else//if-2

MessageBox("
接收数据错误");

}

else//if-1

MessageBox("
串行口打开失败");

}

下位机通信程序:

#include<reg51.h>

#include<stdlib.h>

#include<stdio.h>

#definecount 9

#define com_code 0x00

#definecom_code1 0xff

unsigned char buffer[count];

intpo,year,month,date,hour;

intminute,second,recordID ;

intsum;

main()

{



/*
初始化串口和定时器*/

TMOD=0×20;

TH1=0×fd;

TR1=0×01;

ET1=0×00;

ES=1;

EA=1;

/*
待发送数据送缓冲区*/

buffer[0]=0×ff;//
数据特征码

buffer[1]=count+1;//
数据长度

buffer[2]=year
//

buffer[3]=month;//


buffer[4]=date;//


buffer[5]=hour;//


buffer[6]=minute;//


buffer[7]=second;//


buffer[8]=recordID;//
事件号

for(po=0;po<count;po++)

sum+=buffer[po];

buffer[9]=sum;//
校验和



}

/*
发送中断服务程序*/

voidsend(void) interrupt 4 using 1

{

inti;

RI=0;

EA=0;

do

{

for(i=0;i<=count;i++)

{

SBUF=buffer[i];//
发送数据和校验和//

while(TI==0);

TI=0;

}

while(RI==0);

RI=0;

}while(SBUF!=0);//
主机接收不正确,重新发送//

EA=1


Return;

}

 

5
应用总结

根据不同需要,选择合适的方法。我们选用的用VC++类实现的上位机和下位机的串行通信方法具有使用简单、编写程序方便的特点。经过半年多应用于EBM灭火系统的情况来看,该方法实现的系统运行稳定可靠,是一种值得推广的简单易行的通信方法。

 

参考 文 献

1Kate Gregory Visual C++6
开发使用手册.北京:机械工业出版社,1999

2
何立民.单片机的C语言应用程序设计.北京:北京航空航天大学出版社,1997

3
马风格.VC控件与串行通讯.1999现代计算机,20004



特别声明:文章版权归原作者所有,未经允许请勿转载,如有任何问题请联系我们.









关于VC在线|站点地图|广告投放|本站大事记|版权声明|友情链接|联系我们

Copyright© 2004-2008Inc.All rights reserved.
本站由loose_went制作维护
ICP05015318站长统计




窗体顶端

窗体底端



窗体顶端

标题<>>发布者<>>

窗体底端








480×60广告位虚位以待Email:[email protected]

120×60广告位







首  页

|

技术论坛

|

技术文章

|

程序源码

|

合作项目

|

在线教程

|

资料下载

|

读书笔记

|

发布文章

|

RSS

|

站点地图




当前位置:首页>>技术文章>>网络与通讯协议


文章标题:VC++中用通讯控件开发串行通信程序
原作 者:曾明
原出 处:不详
发布者:loose_went
发布类型:转载
发布日期:2003-12-30
今日/总浏览:1/4679




程序运行效果截图:



本文详细介绍了VisualC++5.0版中有关通讯控件的使用方法,并给出了一个利用该控件编写的简单通用的通信示例程序。

关键词:串行通信      通讯控件

随着Win95的逐步普及,程序员们越来越愿意在Win95下编程,而VisualC++正成为主要的开发工具。然而用VCWin95下开发串行通讯程序是程序员们经常会遇到确又令人头痛的一件事,不但要理解许多复杂的API函数,还要掌握多线程编程。令人高兴的是VisualC++中提供的通讯控件帮助我们解决了这一难题,通过对这一控件的正确使用,我们可以轻松地编写出所需的串行通信程序。

下面,我们将结合一个具体的例子来说明。本程序的编程环境是Win95VisualC++5.0。这个编程示例的功能比较强,它可以由用户选定进行传输的通信端口,并设定这个端口的相关参数,包括波特率、数据位、停止位、奇偶校验和流量控制等。还具有发送数据和接收数据的双重功能。

.  在程序中嵌入通讯控件

启动VisualC++5.0,利用MFCAppWizardexe)新建一个项目文件,并命名为CommTest,在AppWizard第一步选择基于对话框的应用程序类型(Dialogbased),在第二步将ActiveXControls复选框选中,表示本程序支持ActiveX控件。其他均接收缺省设置,AppWizard将自动生成一个以对话框为主窗口的应用程序。下面我们将在该程序中加入通讯控件。在ResourceView中打开对话框(IDD_COMMTEST_DIALOG),将其修改为如图一所示的对话框。注意将对话框的语言属性改为ChineseP.R.C)。
单击Project菜单,从菜单中选择Addto Project,再单击Componentsand Controls,从弹出的对话框中单击RegisteredActiveX Controls,然后在列表框中选择MicrosoftCommunications Controlversion5.0,单击OK。这时会询问你是否加入CMSComm类,单击OK,返回后就会看到控件工具条上添加了一个通讯控件(形状如电话机),将其拖放到对话框上,同时注意到Project中新增加了一个类。通过查看类MSComm的源文件mscomm.cpp,我们可以了解这个控件的属性和使用方法,其中的Get…函数可以用来访问该属性的当前值,而Set…函数则用来设置该属性的新值。

.  设置通讯控件的属性值

激活通讯控件的属性对话框,一些重要的属性及其说明如表一所示:

属 性

设定值

说 明

CommPort

2

串口号,一般串口为鼠标所用,故用串口2

InBufferSize

1024

接收缓冲区大小

InputLen

0

从接收缓冲区读取的字节数,0表示全部读取

InputMode

1

接收数据的类型,0表示文本类型,1表示二进制类型

OutBufferSize

1024

发送缓冲区大小

RThreshold

1

设定当接收几个字符时触发OnComm事件,0表示不产生事件,1表示每接收一个字符就产生一个事件

SThreshold

0

设定在触发OnComm事件前,发送缓冲区内所允许的最少的字符数,0表示发送数据时不产生事件,1表示当发送缓冲区空时产生OnComm事件

Settings

9600,n,8,1

串口的参数设置,依次为波特率、奇偶校验(n-无校验,e-偶校验,o-奇校验)、数据位数、停止位数

1串口属性及说明

上面表中的属性设定值是本例程中所用的值,可根据需要灵活设定。至于其它的属性可采用缺省值,其说明可参考联机帮助文件。

通讯控件的工作原理类似与中断方式,当有通讯事件发生时(如发送数据、接收数据等),就会触发OnComm事件,在该事件的处理函数中调用GetCommEvent()函数,通过返回值即可确定是那类事件,再做出相应的处理。

.  主程序的编制

首先我们为对话框中的控件添加对应的变量和响应函数。具体做法如表二所示:

控件名称

控件ID

对应变量或函数

发送数据编辑框

IDC_SENDDATA

m_SendData

接收数据编辑框

IDC_RECEIVEDATA

m_    ReceiveData

发送按钮

IDC_SEND

OnSend()

清除按钮

IDC_CLEAR

OnClear()

通讯控件

IDC_MSCOMM

m_Comm

2添加控件表

再打开ClassWizard,选中IDC_MSCOMM,可看到该控件能响应的消息为OnComm,添加改函数并将其改名为OnComm(),在该函数中添加代码,就能实现对串口事件的处理。其中一个需要注意的问题是函数GetInput()返回的是VARIANT型变量,而在编辑框中显示的是CString型变量,因此必需进行转换。先将VARIANT型变量转换为COleSafeArray型变量,再将其转换为BYTE型数组,然后将数组转换为CString型变量。这个转换过程看起来比较复杂,但它可以满足用不同的变量类型来显示接收数据。该程序的主要代码添加在CommTestDlg.cpp中,如下所示:

voidCCommTestDlg::OnSend()

{

   if(!m_Comm.GetPortOpen())

       m_Comm.SetPortOpen(TRUE);//
打开串口

   UpdateData(TRUE);

   m_Comm.SetOutput(COleVariant(m_SendData));//
发送数据

}

void CCommTestDlg::OnClear()

{

   m_ReceiveData.Empty();//
清除接收对话框中的数据

   m_SendData.Empty();//
清除发送对话框中的数据

  UpdateData(FALSE);

}

void CCommTestDlg::OnComm()

{

   VARIANT m_Input1;

   COleSafeArray m_Input2;

    longlength,i;

    BYTE data[1024];

    CString str;

   if(m_Comm.GetCommEvent()==2)//
接收缓冲区内有字符

   {

       m_Input1=m_Comm.GetInput();//
读取缓冲区内的数据

       m_Input2=m_Input1;//
VARIANT型变量转换为COleSafeArray型变量

      length=m_Input2.GetOneDimSize();//
确定数据长度

      for(i=0;i<length;i++)

          m_Input2.GetElement(&i,data+i);//
将数据转换为BYTE型数组

      for(i=0;i<length;i++)//
将数组转换为CString型变量

       {

          char a=*(char*)(data+i);

          str.Format("%c",a);

          m_ReceiveData+=str; 

       }

    }

   UpdateData(FALSE);//
更新编辑框内容

}

.  试验程序

现在程序已经写完,可以编译运行。我们可以做一个小实验来验证程序的功能。首先检验你的串口2,看它是否工作正常,可用常见的DOS程序Comdebug检查。确认串口工作正常后,可用串口线将两台微机的串口2相连,同时在两台机子上运行该程序(也可只用一台微机,将其串口22脚和3脚短接,即自发自收状态),在发送对话框中随便敲入一些字符,用鼠标点发送按钮,你就会在另一台机子的接收对话框中看到这些数据,甚至汉字也可以发送。由此可见,利用通讯控件可以很容易的编写出串行通信程序。但相对来说通讯控件在VC中的使用要比在VB中复杂的多,要想开发出更多更灵活的使用方法还需要不断的摸索和实践。

 
 
 
 
 

你可能感兴趣的:(正则表达式,String,null,buffer,input,通讯)