QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 2513|回复: 7

串口编程问题?

[复制链接]
发表于 2004-6-22 09:32:42 | 显示全部楼层 |阅读模式
现在有大问题是这样的:
一台机子发送长度不一定的字符串,我怎样才能完整的接受到呢?read和write好像是没有
缓冲区的,两次read中间可能就把一些数据丢了。而且我一次发送73个字符,而read却一

只能接受20多个,等循环到下一次read时,一些数据不就错过了吗?
有没有办法建立一个缓冲区呢,让发送的数据统统写入缓冲内?
发表于 2004-6-22 17:23:58 | 显示全部楼层
不会,有硬件缓冲区,你发送的数据将一直保留在缓冲区里,直到缓冲区满掉才被新的数据冲走,否则一直留在那里可供读取,读取掉的数据也移出缓冲区(其实没有真正意义上的移出,只是读取指针不再指向那些数据)
在win下可以使用中断来实行数据的即时读取,就是缓冲区一收到新的数据可以触发中断服务程序将收到的新数据立即读取掉,但linux下似乎无法这样做(可能不知道方法),只能用不断循环读取的方法来保证读取到所有的数据
回复

使用道具 举报

发表于 2004-6-25 09:37:39 | 显示全部楼层
在DOS中,我可以这种方式直接实现用串口发送任一字符串,在linux中该怎样做?

     P1 = &H2F8: P2 = &H2F9: P3 = &H2FB: P4 = &H2FD
       OUT P3, 128: OUT P1, 96: OUT P2, 0: OUT P3, 27

     N$=“1234567989”
        T = 0
        FOR I = 1 TO LEN(N$)
F1:
        IF (INP(P4) AND 32) = 0 OR (INP(P4) AND 64) = 0 THEN GOTO F2
        T = 0
        OUT P1, ASC(MID$(N$, I, 1))
        FOR J = 0 TO 10000: NEXT J
        GOTO F3
F2:
        T = T + 1: IF T > 1000 THEN I = 100 ELSE GOTO F1
F3:
        NEXT I
回复

使用道具 举报

发表于 2004-6-26 09:31:47 | 显示全部楼层
先open 串口,再write;
可发email我,给你linux下的串口中断方式的收发example code
回复

使用道具 举报

发表于 2004-6-26 11:26:37 | 显示全部楼层
[quote:2f5b5fb5e6="ddv"]在DOS中,我可以这种方式直接实现用串口发送任一字符串,在linux中该怎样做?

     P1 = &H2F8: P2 = &H2F9: P3 = &H2FB: P4 = &H2FD
       OUT P3, 128: OUT P1, 96: OUT P2, 0: OUT P3, 27

     N$=“1234567989”
        T = 0
        FOR I = 1 TO LEN(N$)
F1:
        IF (INP(P4) AND 32) = 0 OR (INP(P4) AND 64) = 0 THEN GOTO F2
        T = 0
        OUT P1, ASC(MID$(N$, I, 1))
        FOR J = 0 TO 10000: NEXT J
        GOTO F3
F2:
        T = T + 1: IF T > 1000 THEN I = 100 ELSE GOTO F1
F3:
        NEXT I[/quote]

linux下打开端口并初始化成raw后,可以用read(write)来逐个读取(写)字节
请问你上面:
P2 = &H2F9: P3 = &H2FB这两个端口是设置串口参数的吧?
OUT P3, 128: OUT P1, 96: OUT P2, 0: OUT P3, 27这三行是设置成什么样的参数?
P4端口是串口状态,那(INP(P4) AND 32) = 0和(INP(P4) AND 64) = 0 代表什么状态?缓冲已满?
回复

使用道具 举报

发表于 2004-6-26 16:15:05 | 显示全部楼层
[quote:b53ca732e6="llc"][quote:b53ca732e6="ddv"]在DOS中,我可以这种方式直接实现用串口发送任一字符串,在linux中该怎样做?

     P1 = &H2F8: P2 = &H2F9: P3 = &H2FB: P4 = &H2FD
       OUT P3, 128: OUT P1, 96: OUT P2, 0: OUT P3, 27

     N$=“1234567989”
        T = 0
        FOR I = 1 TO LEN(N$)
F1:
        IF (INP(P4) AND 32) = 0 OR (INP(P4) AND 64) = 0 THEN GOTO F2
        T = 0
        OUT P1, ASC(MID$(N$, I, 1))
        FOR J = 0 TO 10000: NEXT J
        GOTO F3
F2:
        T = T + 1: IF T > 1000 THEN I = 100 ELSE GOTO F1
F3:
        NEXT I[/quote]

linux下打开端口并初始化成raw后,可以用read(write)来逐个读取(写)字节
请问你上面:
P2 = &H2F9: P3 = &H2FB这两个端口是设置串口参数的吧?
OUT P3, 128: OUT P1, 96: OUT P2, 0: OUT P3, 27这三行是设置成什么样的参数?
P4端口是串口状态,那(INP(P4) AND 32) = 0和(INP(P4) AND 64) = 0 代表什么状态?缓冲已满?[/quote]
不好意思,我那个是以前用MS-BASIC编的。

P2 = &H2F9    波特率寄存器高位
P3 = &H2FB   线路控制寄存器

OUT P3, 128   置P3最高位为1,以便能访问其他寄存器
OUT P1, 96     置波特率低位寄存器值
OUT P2, 0       置波特率高位寄存器值
以上两行决定了波特率为1200
OUT P3, 27     置控制寄存器值,字符长度8位,1个停止位,无奇偶校验

P4 是线路状态寄存器
(INP(P4) AND 32) = 0    传送保持寄存器不空
(INP(P4) AND 64) = 0    传送转移寄存器不空
以上两个判断可确定字符是否已成功发送,两者均为1才能确定字符已成功发送。


当线路控制寄存器最高位为1或0时,其他寄存器有不同的定义,

当为1时:
2F8 为波特率寄存器低位,只写
2F9 为波特率寄存器高位,只写

当为0时:
2F8 为发送保持寄存器/数据接收寄存器,可读写
2F9 为中断允许寄存器,可读写
回复

使用道具 举报

发表于 2004-6-26 16:17:51 | 显示全部楼层
另外,这是直接对硬件端口进行操作,并不涉及OS。
我曾在WINDOWS下试过,只要能读写端口,照样能发送。
回复

使用道具 举报

发表于 2004-6-27 07:23:05 | 显示全部楼层
[quote:a553624076="ddv"]另外,这是直接对硬件端口进行操作,并不涉及OS。
我曾在WINDOWS下试过,只要能读写端口,照样能发送。[/quote]

在98或95应该可以,但nt内核的系统不支持直接访问硬件端口应该不行

在linux下,对串口的操作还是很方便的,譬如打开串口:
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );


初始化串口:
int setup_com(int fd){
    struct termios options;
    tcgetattr(fd, &options);
    /* Set the baud rates to 38400...*/
    cfsetispeed(&options, B38400);
    cfsetospeed(&options, B38400);
    /* Enable the receiver and set local mode...*/
    options.c_cflag |= (CLOCAL | CREAD);
    /* Set c_cflag options.*/
    options.c_cflag |= PARENB;
    options.c_cflag &= ~PARODD;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;   
    /* Set c_iflag input options */
    options.c_iflag &=~(IXON | IXOFF | IXANY);
    options.c_iflag &=~(INLCR | IGNCR | ICRNL);
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    /* Set c_oflag output options */
    options.c_oflag &= ~OPOST;   
    /* Set the timeout options */
    options.c_cc[VMIN]  = 0;
    options.c_cc[VTIME] = 10;
    tcsetattr(fd, TCSANOW, &options);
    return 1;
}

其中:
c_cflag  : 控制选项
c_lflag  : 线选项
c_iflag  : 输入选项
c_oflag  :输出选项
c_cc    :控制字符
c_ispeed :输入数据波特率
c_ospeed :输出数据波特率
c_cflag: CLOCAL 本地模式,不改变端口的所有者
         CREAD  表示使能数据接收器
         PARENB  表示偶校验
         PARODD 表示奇校验
CSTOPB  使用两个停止位
CSIZE    对数据的bit使用掩码
CS8      数据宽度是8bit
c_lflag:  ICANON 使能规范输入,否则使用原始数据(本文使用)
ECHO    回送(echo)输入数据
ECHOE   回送擦除字符
ISIG      使能SIGINTR,SIGSUSP, SIGDSUSP和 SIGQUIT 信号
c_iflag:  IXON     使能输出软件控制
         IXOFF    使能输入软件控制
         IXANY    允许任何字符再次开启数据流
         INLCR    把字符NL(0A)映射到CR(0D)
         IGNCR    忽略字符CR(0D)
       ICRNL    把CR(0D)映射成字符NR(0A)
    c_oflag: OPOST  输出后处理,如果不设置表示原始数据(本文使用原始数据raw)
c_cc[VMIN]:  最少可读数据
c_cc[VTIME]: 等待数据时间(10秒的倍数)

初始化完后就可以直接对串口像普通文件一样使用文件操作函数read和write进行读写操作了,read和write函数返回的是成功收到(送出)的字节数,如果成功那么返回值大于零,很容易判断;
不过要注意的是:linux下对串口的read是半阻塞式的,不设置好VMIN和VTIME的话,会导致执行read时发生明显的阻塞现象,上面的VMIN0和VTIME10设置成只要串口收到数据read就立即返回数据,如果一直没收到数据,则10秒后read超时才返回0值;如果VMIN和VTIME不设置的话,缺省值好像是60秒后read才能超时返回,在60秒内如果串口没收到数据将一直阻塞,看上去程序像死掉一样;
如果需要read无论有没有收到数据都立即返回,可以初始化成不延迟方式:
fcntl(fd, F_SETFL, FNDELAY);
这样执行到read时如果收到数据则返回成功收到的字节数,如果没收到数据则立即返回0,不会产生阻塞现象。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-21 22:21 , Processed in 0.076827 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表