﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-嵌入式-文章分类-驱动程序开发技术资料</title><link>http://www.cnitblog.com/zouzheng/category/2081.html</link><description>要像阿甘一直向前奔跑！</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 12:49:10 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 12:49:10 GMT</pubDate><ttl>60</ttl><item><title>TERMIOS- </title><link>http://www.cnitblog.com/zouzheng/articles/25472.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 09 Apr 2007 06:51:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25472.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25472.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25472.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25472.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25472.html</trackback:ping><description><![CDATA[<p>TERMIOS<br>--------------------------------------------------------------------------------<br>&nbsp; <br>NAME<br>termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed - 获取和设置终端属性，行控制，获取和设置波特率&nbsp;&nbsp; <br>SYNOPSIS 总览<br>#include &lt;termios.h&gt; <br>#include &lt;unistd.h&gt; <br>int tcgetattr(int </p>
<p>int tcsetattr(int fd, int optional_actions, struct termios *termios_p); </p>
<p>int tcsendbreak(int fd, int duration); </p>
<p>int tcdrain(int fd); </p>
<p>int tcflush(int fd, int queue_selector); </p>
<p>int tcflow(int fd, int action); </p>
<p>int cfmakeraw(struct termios *termios_p); </p>
<p>speed_t cfgetispeed(struct termios *termios_p); </p>
<p>speed_t cfgetospeed(struct termios *termios_p); </p>
<p>int cfsetispeed(struct termios *termios_p, speed_t speed); </p>
<p>int cfsetospeed(struct termios *termios_p, speed_t speed);&nbsp;&nbsp; </p>
<p>DESCRIPTION 描述<br>termios 函数族提供了一个常规的终端接口，用于控制非同步通信端口。 <br>这里描述的大部分属性有一个 termios_p 类型的参数，它是指向一个 termios 结构的指针。这个结构包含了至少下列成员： </p>
<p><br>tcflag_t c_iflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 输入模式 */<br>tcflag_t c_oflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 输出模式 */<br>tcflag_t c_cflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 控制模式 */<br>tcflag_t c_lflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 本地模式 */<br>cc_t c_cc[NCCS];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 控制字符 */</p>
<p>c_iflag 标志常量： </p>
<p>IGNBRK <br>忽略输入中的 BREAK 状态。 <br>BRKINT <br>如果设置了 IGNBRK，将忽略 BREAK。如果没有设置，但是设置了 BRKINT，那么 BREAK 将使得输入和输出队列被刷新，如果终端是一个前台进程组的控制终端，这个进程组中所有进程将收到 SIGINT 信号。如果既未设置 IGNBRK 也未设置 BRKINT，BREAK 将视为与 NUL 字符同义，除非设置了 PARMRK，这种情况下它被视为序列 \377 \0 \0。 <br>IGNPAR <br>忽略桢错误和奇偶校验错。 <br>PARMRK <br>如果没有设置 IGNPAR，在有奇偶校验错或桢错误的字符前插入 \377 \0。如果既没有设置 IGNPAR 也没有设置 PARMRK，将有奇偶校验错或桢错误的字符视为 \0。 <br>INPCK <br>启用输入奇偶检测。 <br>ISTRIP <br>去掉第八位。 <br>INLCR <br>将输入中的 NL 翻译为 CR。 <br>IGNCR <br>忽略输入中的回车。 <br>ICRNL <br>将输入中的回车翻译为新行 (除非设置了 IGNCR)。 <br>IUCLC <br>(不属于 POSIX) 将输入中的大写字母映射为小写字母。 <br>IXON <br>启用输出的 XON/XOFF 流控制。 <br>IXANY <br>(不属于 POSIX.1；XSI) 允许任何字符来重新开始输出。(?) <br>IXOFF <br>启用输入的 XON/XOFF 流控制。 <br>IMAXBEL <br>(不属于 POSIX) 当输入队列满时响零。Linux 没有实现这一位，总是将它视为已设置。 <br>POSIX.1 中定义的 c_oflag 标志常量： </p>
<p>OPOST <br>启用具体实现自行定义的输出处理。 <br>其余 c_oflag 标志常量定义在 POSIX 1003.1-2001 中，除非另外说明。 </p>
<p>OLCUC <br>(不属于 POSIX) 将输出中的小写字母映射为大写字母。 <br>ONLCR <br>(XSI) 将输出中的新行符映射为回车-换行。 <br>OCRNL <br>将输出中的回车映射为新行符 <br>ONOCR <br>不在第 0 列输出回车。 <br>ONLRET <br>不输出回车。 <br>OFILL <br>发送填充字符作为延时，而不是使用定时来延时。 <br>OFDEL <br>(不属于 POSIX) 填充字符是 ASCII DEL (0177)。如果不设置，填充字符则是 ASCII NUL。 <br>NLDLY <br>新行延时掩码。取值为 NL0 和 NL1。 <br>CRDLY <br>回车延时掩码。取值为 CR0, CR1, CR2, 或 CR3。 <br>TABDLY <br>水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值为 TAB3，即 XTABS，将扩展跳格为空格 (每个跳格符填充 8 个空格)。(?) <br>BSDLY <br>回退延时掩码。取值为 BS0 或 BS1。(从来没有被实现过) <br>VTDLY <br>竖直跳格延时掩码。取值为 VT0 或 VT1。 <br>FFDLY <br>进表延时掩码。取值为 FF0 或 FF1。 <br>c_cflag 标志常量： </p>
<p>CBAUD <br>(不属于 POSIX) 波特率掩码 (4+1 位)。 <br>CBAUDEX <br>(不属于 POSIX) 扩展的波特率掩码 (1 位)，包含在 CBAUD 中。 <br>(POSIX 规定波特率存储在 termios 结构中，并未精确指定它的位置，而是提供了函数 cfgetispeed() 和 cfsetispeed() 来存取它。一些系统使用 c_cflag 中 CBAUD 选择的位，其他系统使用单独的变量，例如 sg_ispeed 和 sg_ospeed 。) </p>
<p>CSIZE <br>字符长度掩码。取值为 CS5, CS6, CS7, 或 CS8。 <br>CSTOPB <br>设置两个停止位，而不是一个。 <br>CREAD <br>打开接受者。 <br>PARENB <br>允许输出产生奇偶信息以及输入的奇偶校验。 <br>PARODD <br>输入和输出是奇校验。 <br>HUPCL <br>在最后一个进程关闭设备后，降低 modem 控制线 (挂断)。(?) <br>CLOCAL <br>忽略 modem 控制线。 <br>LOBLK <br>(不属于 POSIX) 从非当前 shell 层阻塞输出(用于 shl )。(?) <br>CIBAUD <br>(不属于 POSIX) 输入速度的掩码。CIBAUD 各位的值与 CBAUD 各位相同，左移了 IBSHIFT 位。 <br>CRTSCTS <br>(不属于 POSIX) 启用 RTS/CTS (硬件) 流控制。 <br>c_lflag 标志常量： </p>
<p>ISIG <br>当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时，产生相应的信号。 <br>ICANON <br>启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE，以及按行的缓冲。 <br>XCASE <br>(不属于 POSIX; Linux 下不被支持) 如果同时设置了 ICANON，终端只有大写。输入被转换为小写，除了以 \ 前缀的字符。输出时，大写字符被前缀 \，小写字符被转换成大写。 <br>ECHO <br>回显输入字符。 <br>ECHOE <br>如果同时设置了 ICANON，字符 ERASE 擦除前一个输入字符，WERASE 擦除前一个词。 <br>ECHOK <br>如果同时设置了 ICANON，字符 KILL 删除当前行。 <br>ECHONL <br>如果同时设置了 ICANON，回显字符 NL，即使没有设置 ECHO。 <br>ECHOCTL <br>(不属于 POSIX) 如果同时设置了 ECHO，除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大 0x40 的 ASCII 码。例如，字符 0x08 (BS) 被回显为 ^H。 <br>ECHOPRT <br>(不属于 POSIX) 如果同时设置了 ICANON 和 IECHO，字符在删除的同时被打印。 <br>ECHOKE <br>(不属于 POSIX) 如果同时设置了 ICANON，回显 KILL 时将删除一行中的每个字符，如同指定了 ECHOE 和 ECHOPRT 一样。 <br>DEFECHO <br>(不属于 POSIX) 只在一个进程读的时候回显。 <br>FLUSHO <br>(不属于 POSIX; Linux 下不被支持) 输出被刷新。这个标志可以通过键入字符 DISCARD 来开关。 <br>NOFLSH <br>禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列。 <br>TOSTOP <br>向试图写控制终端的后台进程组发送 SIGTTOU 信号。 <br>PENDIN <br>(不属于 POSIX; Linux 下不被支持) 在读入下一个字符时，输入队列中所有字符被重新输出。(bash 用它来处理 typeahead) <br>IEXTEN <br>启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用，才能解释特殊字符 EOL2，LNEXT，REPRINT 和 WERASE，IUCLC 标志才有效。 <br>c_cc 数组定义了特殊的控制字符。符号下标 (初始值) 和意义为： </p>
<p>VINTR <br>(003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中断字符。发出 SIGINT 信号。当设置 ISIG 时可被识别，不再作为输入传递。 <br>VQUIT <br>(034, FS, Ctrl-\) 退出字符。发出 SIGQUIT 信号。当设置 ISIG 时可被识别，不再作为输入传递。 <br>VERASE <br>(0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 删除字符。删除上一个还没有删掉的字符，但不删除上一个 EOF 或行首。当设置 ICANON 时可被识别，不再作为输入传递。 <br>VKILL <br>(025, NAK, Ctrl-U, or Ctrl-X, or also @) 终止字符。删除自上一个 EOF 或行首以来的输入。当设置 ICANON 时可被识别，不再作为输入传递。 <br>VEOF <br>(004, EOT, Ctrl-D) 文件尾字符。更精确地说，这个字符使得 tty 缓冲中的内容被送到等待输入的用户程序中，而不必等到 EOL。如果它是一行的第一个字符，那么用户程序的 read() 将返回 0，指示读到了 EOF。当设置 ICANON 时可被识别，不再作为输入传递。 <br>VMIN <br>非 canonical 模式读的最小字符数。 <br>VEOL <br>(0, NUL) 附加的行尾字符。当设置 ICANON 时可被识别。 <br>VTIME <br>非 canonical 模式读时的延时，以十分之一秒为单位。 <br>VEOL2 <br>(not in POSIX; 0, NUL) 另一个行尾字符。当设置 ICANON 时可被识别。 <br>VSWTCH <br>(not in POSIX; not supported under Linux; 0, NUL) 开关字符。(只为 shl 所用。) <br>VSTART <br>(021, DC1, Ctrl-Q) 开始字符。重新开始被 Stop 字符中止的输出。当设置 IXON 时可被识别，不再作为输入传递。 <br>VSTOP <br>(023, DC3, Ctrl-S) 停止字符。停止输出，直到键入 Start 字符。当设置 IXON 时可被识别，不再作为输入传递。 <br>VSUSP <br>(032, SUB, Ctrl-Z) 挂起字符。发送 SIGTSTP 信号。当设置 ISIG 时可被识别，不再作为输入传递。 <br>VDSUSP <br>(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延时挂起信号。当用户程序读到这个字符时，发送 SIGTSTP 信号。当设置 IEXTEN 和 ISIG，并且系统支持作业管理时可被识别，不再作为输入传递。 <br>VLNEXT <br>(not in POSIX; 026, SYN, Ctrl-V) 字面上的下一个。引用下一个输入字符，取消它的任何特殊含义。当设置 IEXTEN 时可被识别，不再作为输入传递。 <br>VWERASE <br>(not in POSIX; 027, ETB, Ctrl-W) 删除词。当设置 ICANON 和 IEXTEN 时可被识别，不再作为输入传递。 <br>VREPRINT <br>(not in POSIX; 022, DC2, Ctrl-R) 重新输出未读的字符。当设置 ICANON 和 IEXTEN 时可被识别，不再作为输入传递。 <br>VDISCARD <br>(not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 开关：开始/结束丢弃未完成的输出。当设置 IEXTEN 时可被识别，不再作为输入传递。 <br>VSTATUS <br>(not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T). <br>这些符号下标值是互不相同的，除了 VTIME，VMIN 的值可能分别与 VEOL，VEOF 相同。 (在 non-canonical 模式下，特殊字符的含义更改为延时含义。MIN 表示应当被读入的最小字符数。TIME 是以十分之一秒为单位的计时器。如果同时设置了它们，read 将等待直到至少读入一个字符，一旦读入 MIN 个字符或者从上次读入字符开始经过了 TIME 时间就立即返回。如果只设置了 MIN，read 在读入 MIN 个字符之前不会返回。如果只设置了 TIME，read 将在至少读入一个字符，或者计时器超时的时候立即返回。如果都没有设置，read 将立即返回，只给出当前准备好的字符。) (?) </p>
<p>tcgetattr() 得到与 fd 指向的对象相关的参数，将它们保存于 termios_p 引用的 termios 结构中。函数可以从后台进程中调用；但是，终端属性可能被后来的前台进程所改变。 </p>
<p>tcsetattr() 设置与终端相关的参数 (除非需要底层支持却无法满足)，使用 termios_p 引用的 termios 结构。optional_actions 指定了什么时候改变会起作用： </p>
<p>TCSANOW <br>改变立即发生 <br>TCSADRAIN <br>改变在所有写入 fd 的输出都被传输后生效。这个函数应当用于修改影响输出的参数时使用。 <br>TCSAFLUSH <br>改变在所有写入 fd 引用的对象的输出都被传输后生效，所有已接受但未读入的输入都在改变发生前丢弃。 <br>tcsendbreak() 传送连续的 0 值比特流，持续一段时间，如果终端使用异步串行数据传输的话。如果 duration 是 0，它至少传输 0.25 秒，不会超过 0.5 秒。如果 duration 非零，它发送的时间长度由实现定义。 </p>
<p>如果终端并非使用异步串行数据传输，tcsendbreak() 什么都不做。 </p>
<p>tcdrain() 等待直到所有写入 fd 引用的对象的输出都被传输。 </p>
<p>tcflush() 丢弃要写入 引用的对象，但是尚未传输的数据，或者收到但是尚未读取的数据，取决于 queue_selector 的值： </p>
<p>TCIFLUSH <br>刷新收到的数据但是不读 <br>TCOFLUSH <br>刷新写入的数据但是不传送 <br>TCIOFLUSH <br>同时刷新收到的数据但是不读，并且刷新写入的数据但是不传送 <br>tcflow() 挂起 fd 引用的对象上的数据传输或接收，取决于 action 的值： </p>
<p>TCOOFF <br>挂起输出 <br>TCOON <br>重新开始被挂起的输出 <br>TCIOFF <br>发送一个 STOP 字符，停止终端设备向系统传送数据 <br>TCION <br>发送一个 START 字符，使终端设备向系统传输数据 <br>打开一个终端设备时的默认设置是输入和输出都没有挂起。 </p>
<p>波特率函数被用来获取和设置 termios 结构中，输入和输出波特率的值。新值不会马上生效，直到成功调用了 tcsetattr() 函数。 </p>
<p>设置速度为 B0 使得 modem "挂机"。与 B38400 相应的实际比特率可以用 setserial(8) 调整。 </p>
<p>输入和输出波特率被保存于 termios 结构中。 </p>
<p>cfmakeraw 设置终端属性如下： </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; termios_p-&gt;c_iflag &amp;= ~(IGNBRK|BRKINT|PARMRK|ISTRIP<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |INLCR|IGNCR|ICRNL|IXON);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; termios_p-&gt;c_oflag &amp;= ~OPOST;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; termios_p-&gt;c_lflag &amp;= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; termios_p-&gt;c_cflag &amp;= ~(CSIZE|PARENB);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; termios_p-&gt;c_cflag |= CS8;</p>
<p>cfgetospeed() 返回 termios_p 指向的 termios 结构中存储的输出波特率 </p>
<p>cfsetospeed() 设置 termios_p 指向的 termios 结构中存储的输出波特率为 speed。取值必须是以下常量之一： </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B50<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B75<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B110<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B134<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B150<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B200<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B300<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B600<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B1200<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B1800<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B2400<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B4800<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B9600<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B19200<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B38400<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B57600<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B115200<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B230400</p>
<p>零值 B0 用来中断连接。如果指定了 B0，不应当再假定存在连接。通常，这样将断开连接。CBAUDEX 是一个掩码，指示高于 POSIX.1 定义的速度的那一些 (57600 及以上)。因此，B57600 &amp; CBAUDEX 为非零。 <br>cfgetispeed() 返回 termios 结构中存储的输入波特率。 </p>
<p>cfsetispeed() 设置 termios 结构中存储的输入波特率为 speed。如果输入波特率被设为0，实际输入波特率将等于输出波特率。&nbsp;&nbsp; </p>
<p>RETURN VALUE 返回值<br>cfgetispeed() 返回 termios 结构中存储的输入波特率。 </p>
<p>cfgetospeed() 返回 termios 结构中存储的输出波特率。 </p>
<p>其他函数返回： </p>
<p>0 <br>成功 <br>-1 <br>失败，并且为 errno 置值来指示错误。 <br>注意 tcsetattr() 返回成功，如果任何所要求的修改可以实现的话。因此，当进行多重修改时，应当在这个函数之后再次调用 tcgetattr() 来检测是否所有修改都成功实现。 </p>
<p><br>NOTES 注意<br>Unix V7 以及很多后来的系统有一个波特率的列表，在十四个值 B0, ..., B9600 之后可以看到两个常数 EXTA, EXTB ("External A" and "External B")。很多系统将这个列表扩展为更高的波特率。 <br>tcsendbreak 中非零的 duration 有不同的效果。SunOS 指定中断 duration*N 秒，其中 N 至少为 0.25，不高于 0.5 。Linux, AIX, DU, Tru64 发送 duration 微秒的 break 。FreeBSD, NetBSD, HP-UX 以及 MacOS 忽略 duration 的值。在 Solaris 和 Unixware 中， tcsendbreak 搭配非零的 duration 效果类似于 tcdrain。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25472.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-09 14:51 <a href="http://www.cnitblog.com/zouzheng/articles/25472.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>termios.h - define values for termios </title><link>http://www.cnitblog.com/zouzheng/articles/25470.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 09 Apr 2007 06:38:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25470.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25470.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25470.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25470.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25470.html</trackback:ping><description><![CDATA[SYNOPSIS
<blockquote>
<pre><code>
#include &lt;termios.h&gt;
</code>
</pre>
</blockquote>
<h4><a name=tag_000_009_539>&nbsp;</a>DESCRIPTION</h4>
<blockquote>The <em>&lt;termios.h&gt;</em> header contains the definitions used by the terminal I/O interfaces (see the <strong>XBD</strong> specification, <a href="http://opengroup.org/onlinepubs/007908799/xbd/termios.html"><u><font color=#800080><strong>General Terminal Interface</strong>&nbsp;</font></u></a> for the structures and names defined).
<h5><a name=tag_000_009_539_001>&nbsp;</a>The termios Structure</h5>
The following data types are defined through <strong>typedef</strong>:
<dl compact>
<dt><strong>cc_t</strong>
<dd><index term="data type, cc_t"></index>Used for terminal special characters.
<dt><strong>speed_t</strong>
<dd><index term="data type, speed_t"></index>Used for terminal baud rates.
<dt><strong>tcflag_t</strong>
<dd><index term="data type, tcflag_t"></index>Used for terminal modes. </dd></dl>
<p>The above types are all unsigned integral types.
<p>The <strong>termios</strong> structure is defined, and includes at least the following members:
<pre><code>
tcflag_t  c_iflag     input modes
tcflag_t  c_oflag     output modes
tcflag_t  c_cflag     control modes
tcflag_t  c_lflag     local modes
cc_t      c_cc[NCCS]  control chars
</code>
</pre>
<p>A definition is given for:
<dl compact>
<dt>NCCS
<dd>Size of the array <strong>c_cc</strong> for control characters. </dd></dl>
<p>The following subscript names for the array <strong>c_cc</strong> are defined:
<p>
<table borderColor=#000000 align=center border=1>
    <tbody>
        <tr vAlign=top>
            <th align=middle colSpan=2><strong>Subscript Usage</strong>
            <th align=left>&nbsp;
            <tr vAlign=top>
                <th align=middle><strong>Canonical Mode</strong>
                <th align=middle><strong>Non-canonical Mode</strong>
                <th align=middle><strong>Description</strong>
                <tr vAlign=top>
                    <td align=left>VEOF
                    <td align=left>&nbsp;
                    <td align=left>EOF character
                    <tr vAlign=top>
                        <td align=left>VEOL
                        <td align=left>&nbsp;
                        <td align=left>EOL character
                        <tr vAlign=top>
                            <td align=left>VERASE
                            <td align=left>&nbsp;
                            <td align=left>ERASE character
                            <tr vAlign=top>
                                <td align=left>VINTR
                                <td align=left>VINTR
                                <td align=left>INTR character
                                <tr vAlign=top>
                                    <td align=left>VKILL
                                    <td align=left>&nbsp;
                                    <td align=left>KILL character
                                    <tr vAlign=top>
                                        <td align=left>&nbsp;
                                        <td align=left>VMIN
                                        <td align=left>MIN value
                                        <tr vAlign=top>
                                            <td align=left>VQUIT
                                            <td align=left>VQUIT
                                            <td align=left>QUIT character
                                            <tr vAlign=top>
                                                <td align=left>VSTART
                                                <td align=left>VSTART
                                                <td align=left>START character
                                                <tr vAlign=top>
                                                    <td align=left>VSTOP
                                                    <td align=left>VSTOP
                                                    <td align=left>STOP character
                                                    <tr vAlign=top>
                                                        <td align=left>VSUSP
                                                        <td align=left>VSUSP
                                                        <td align=left>SUSP character
                                                        <tr vAlign=top>
                                                            <td align=left>
                                                            <td align=left>VTIME
                                                            <td align=left>TIME value </td>
                                                        </tr>
                                                    </tbody>
                                                </table>
                                                The subscript values are unique, except that the VMIN and VTIME subscripts may have the same values as the VEOF and VEOL subscripts, respectively.
                                                <h5><a name=tag_000_009_539_002>&nbsp;</a>Input Modes</h5>
                                                The <strong>c_iflag</strong> field describes the basic terminal input control:
                                                <dl compact>
                                                <dt>BRKINT
                                                <dd>Signal interrupt on break.
                                                <dt>ICRNL
                                                <dd>Map CR to NL on input.
                                                <dt>IGNBRK
                                                <dd>Ignore break condition.
                                                <dt>IGNCR
                                                <dd>Ignore CR
                                                <dt>IGNPAR
                                                <dd>Ignore characters with parity errors.
                                                <dt>INLCR
                                                <dd>Map NL to CR on input.
                                                <dt>INPCK
                                                <dd>Enable input parity check.
                                                <dt>ISTRIP
                                                <dd>Strip character
                                                <dt>IUCLC
                                                <dd>Map upper-case to lower-case on input (<strong>LEGACY</strong>).
                                                <dt>IXANY
                                                <dd>Enable any character to restart output.
                                                <dt>IXOFF
                                                <dd>Enable start/stop input control.
                                                <dt>IXON
                                                <dd>Enable start/stop output control.
                                                <dt>PARMRK
                                                <dd>Mark parity errors. </dd></dl>
                                                <h5><a name=tag_000_009_539_003>&nbsp;</a>Output Modes</h5>
                                                The <strong>c_oflag</strong> field specifies the system treatment of output:
                                                <dl compact>
                                                <dt>OPOST
                                                <dd>Post-process output
                                                <dt>OLCUC
                                                <dd>Map lower-case to upper-case on output (<strong>LEGACY</strong>).
                                                <dt>ONLCR
                                                <dd>Map NL to CR-NL on output.
                                                <dt>OCRNL
                                                <dd>Map CR to NL on output.
                                                <dt>ONOCR
                                                <dd>No CR output at column 0.
                                                <dt>ONLRET
                                                <dd>NL performs CR function.
                                                <dt>OFILL
                                                <dd>Use fill characters for delay.
                                                <dt>NLDLY
                                                <dd>Select newline delays:
                                                <dl compact>
                                                <dt>NL0
                                                <dd>Newline character type 0.
                                                <dt>NL1
                                                <dd>Newline character type 1. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>CRDLY
                                                <dd>Select carriage-return delays:
                                                <dl compact>
                                                <dt>CR0
                                                <dd>Carriage-return delay type 0.
                                                <dt>CR1
                                                <dd>Carriage-return delay type 1.
                                                <dt>CR2
                                                <dd>Carriage-return delay type 2.
                                                <dt>CR3
                                                <dd>Carriage-return delay type 3. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>TABDLY
                                                <dd>Select horizontal-tab delays:
                                                <dl compact>
                                                <dt>TAB0
                                                <dd>Horizontal-tab delay type 0.
                                                <dt>TAB1
                                                <dd>Horizontal-tab delay type 1.
                                                <dt>TAB2
                                                <dd>Horizontal-tab delay type 2.
                                                <dt>TAB3
                                                <dd>Expand tabs to spaces. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>BSDLY
                                                <dd>Select backspace delays:
                                                <dl compact>
                                                <dt>BS0
                                                <dd>Backspace-delay type 0.
                                                <dt>BS1
                                                <dd>Backspace-delay type 1. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>VTDLY
                                                <dd>Select vertical-tab delays:
                                                <dl compact>
                                                <dt>VT0
                                                <dd>Vertical-tab delay type 0.
                                                <dt>VT1
                                                <dd>Vertical-tab delay type 1. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>FFDLY
                                                <dd>Select form-feed delays:
                                                <dl compact>
                                                <dt>FF0
                                                <dd>Form-feed delay type 0.
                                                <dt>FF1
                                                <dd>Form-feed delay type 1. </dd></dl>
                                                <p>&#160;</p>
                                                </dd></dl>
                                                <h5><a name=tag_000_009_539_004>&nbsp;</a>Baud Rate Selection</h5>
                                                The input and output baud rates are stored in the <strong>termios</strong> structure. These are the valid values for objects of type <strong>speed_t</strong>. The following values are defined, but not all baud rates need be supported by the underlying hardware.
                                                <dl compact>
                                                <dt>B0
                                                <dd>Hang up
                                                <dt>B50
                                                <dd>50 baud
                                                <dt>B75
                                                <dd>75 baud
                                                <dt>B110
                                                <dd>110 baud
                                                <dt>B134
                                                <dd>134.5 baud
                                                <dt>B150
                                                <dd>150 baud
                                                <dt>B200
                                                <dd>200 baud
                                                <dt>B300
                                                <dd>300 baud
                                                <dt>B600
                                                <dd>600 baud
                                                <dt>B1200
                                                <dd>1200 baud
                                                <dt>B1800
                                                <dd>1800 baud
                                                <dt>B2400
                                                <dd>2400 baud
                                                <dt>B4800
                                                <dd>4800 baud
                                                <dt>B9600
                                                <dd>9600 baud
                                                <dt>B19200
                                                <dd>19200 baud
                                                <dt>B38400
                                                <dd>38400 baud </dd></dl>
                                                <h5><a name=tag_000_009_539_005>&nbsp;</a>Control Modes</h5>
                                                The <strong>c_cflag</strong> field describes the hardware control of the terminal; not all values specified are required to be supported by the underlying hardware:
                                                <dl compact>
                                                <dt>CSIZE
                                                <dd>Character size:
                                                <dl compact>
                                                <dt>CS5
                                                <dd>5 bits.
                                                <dt>CS6
                                                <dd>6 bits.
                                                <dt>CS7
                                                <dd>7 bits.
                                                <dt>CS8
                                                <dd>8 bits. </dd></dl>
                                                <p>
                                                <p>&#160;</p>
                                                <dt>CSTOPB
                                                <dd>Send two stop bits, else one.
                                                <p>&#160;</p>
                                                <dt>CREAD
                                                <dd>Enable receiver.
                                                <p>&#160;</p>
                                                <dt>PARENB
                                                <dd>Parity enable.
                                                <p>&#160;</p>
                                                <dt>PARODD
                                                <dd>Odd parity, else even.
                                                <p>&#160;</p>
                                                <dt>HUPCL
                                                <dd>Hang up on last close.
                                                <p>&#160;</p>
                                                <dt>CLOCAL
                                                <dd>Ignore modem status lines.
                                                <p>&#160;</p>
                                                </dd></dl>
                                                <h5><a name=tag_000_009_539_006>&nbsp;</a>Local Modes</h5>
                                                The <strong>c_lflag</strong> field of the argument structure is used to control various terminal functions:
                                                <dl compact>
                                                <dt>ECHO
                                                <dd>Enable echo.
                                                <dt>ECHOE
                                                <dd>Echo erase character as error-correcting backspace.
                                                <dt>ECHOK
                                                <dd>Echo KILL.
                                                <dt>ECHONL
                                                <dd>Echo NL.
                                                <dt>ICANON
                                                <dd>Canonical input (erase and kill processing).
                                                <dt>IEXTEN
                                                <dd>Enable extended input character processing.
                                                <dt>ISIG
                                                <dd>Enable signals.
                                                <dt>NOFLSH
                                                <dd>Disable flush after interrupt or quit.
                                                <dt>TOSTOP
                                                <dd>Send SIGTTOU for background output.
                                                <dt>XCASE
                                                <dd>Canonical upper/lower presentation (<strong>LEGACY</strong>). </dd></dl>
                                                <h5><a name=tag_000_009_539_007>&nbsp;</a>Attribute Selection</h5>
                                                The following symbolic constants for use with <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcsetattr.html"><u><font color=#0000ff>tcsetattr()</font></u></a></em> are defined:
                                                <dl compact>
                                                <dt>TCSANOW
                                                <dd>Change attributes immediately.
                                                <dt>TCSADRAIN
                                                <dd>Change attributes when output has drained.
                                                <dt>TCSAFLUSH
                                                <dd>Change attributes when output has drained; also flush pending input. </dd></dl>
                                                <h5><a name=tag_000_009_539_008>&nbsp;</a>Line Control</h5>
                                                The following symbolic constants for use with <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflush.html"><u><font color=#0000ff>tcflush()</font></u></a></em> are defined:
                                                <dl compact>
                                                <dt>TCIFLUSH
                                                <dd>Flush pending input. Flush untransmitted output.
                                                <dt>TCIOFLUSH
                                                <dd>Flush both pending input and untransmitted output.
                                                <dt>TCOFLUSH
                                                <dd>Flush untransmitted output. </dd></dl>
                                                <p>The following symbolic constants for use with <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflow.html"><u><font color=#0000ff>tcflow()</font></u></a></em> are defined:
                                                <dl compact>
                                                <dt>TCIOFF
                                                <dd>Transmit a STOP character, intended to suspend input data.
                                                <dt>TCION
                                                <dd>Transmit a START character, intended to restart input data.
                                                <dt>TCOOFF
                                                <dd>Suspend output.
                                                <dt>TCOON
                                                <dd>Restart output. </dd></dl>
                                                <p>The following are declared as functions and may also be defined as macros. Function prototypes must be provided for use with an ISO C compiler.
                                                <pre><code>
                                                speed_t <a href="http://opengroup.org/onlinepubs/007908799/xsh/cfgetispeed.html"><u><font color=#0000ff>cfgetispeed</font></u></a>(const struct termios *);
                                                speed_t <a href="http://opengroup.org/onlinepubs/007908799/xsh/cfgetospeed.html"><u><font color=#0000ff>cfgetospeed</font></u></a>(const struct termios *);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/cfsetispeed.html"><u><font color=#0000ff>cfsetispeed</font></u></a>(struct termios *, speed_t);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/cfsetospeed.html"><u><font color=#0000ff>cfsetospeed</font></u></a>(struct termios *, speed_t);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcdrain.html"><u><font color=#0000ff>tcdrain</font></u></a>(int);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflow.html"><u><font color=#0000ff>tcflow</font></u></a>(int, int);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflush.html"><u><font color=#0000ff>tcflush</font></u></a>(int, int);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcgetattr.html"><u><font color=#0000ff>tcgetattr</font></u></a>(int, struct termios *);
                                                pid_t   <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcgetsid.html"><u><font color=#0000ff>tcgetsid</font></u></a>(int);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcsendbreak.html"><u><font color=#0000ff>tcsendbreak</font></u></a>(int, int);
                                                int     <a href="http://opengroup.org/onlinepubs/007908799/xsh/tcsetattr.html"><u><font color=#0000ff>tcsetattr</font></u></a>(int, int, struct termios *);
                                                </code>
                                                </pre>
                                                </blockquote>
                                                <h4><a name=tag_000_009_540>&nbsp;</a>APPLICATION USAGE</h4>
                                                <blockquote>The following names are commonly used as extensions to the above, therefore portable applications must not use them:
                                                <p>
                                                <table <tr valign="top">
                                                    <tbody>
                                                        <tr>
                                                            <th align=left>CBAUD
                                                            <th align=left>EXTB
                                                            <th align=left>VDSUSP
                                                            <tr vAlign=top>
                                                                <td align=left>DEFECHO
                                                                <td align=left>FLUSHO
                                                                <td align=left>VLNEXT
                                                                <tr vAlign=top>
                                                                    <td align=left>ECHOCTL
                                                                    <td align=left>LOBLK
                                                                    <td align=left>VREPRINT
                                                                    <tr vAlign=top>
                                                                        <td align=left>ECHOKE
                                                                        <td align=left>PENDIN
                                                                        <td align=left>VSTATUS
                                                                        <tr vAlign=top>
                                                                            <td align=left>ECHOPRT
                                                                            <td align=left>SWTCH
                                                                            <td align=left>VWERASE
                                                                            <tr vAlign=top>
                                                                                <td align=left>EXTA
                                                                                <td align=left>VDISCARD
                                                                                <td align=left></td>
                                                                            </tr>
                                                                        </tbody>
                                                                    </table>
                                                                    </p>
                                                                    </blockquote>
                                                                    <h4><a name=tag_000_009_541>&nbsp;</a>FUTURE DIRECTIONS</h4>
                                                                    <blockquote>None. </blockquote>
                                                                    <h4><a name=tag_000_009_542>&nbsp;</a>SEE ALSO</h4>
                                                                    <blockquote><em><a href="http://opengroup.org/onlinepubs/007908799/xsh/cfgetispeed.html"><u><font color=#0000ff>cfgetispeed()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/cfgetospeed.html"><u><font color=#0000ff>cfgetospeed()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/cfsetispeed.html"><u><font color=#0000ff>cfsetispeed()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/cfsetospeed.html"><u><font color=#0000ff>cfsetospeed()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcdrain.html"><u><font color=#0000ff>tcdrain()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflow.html"><u><font color=#0000ff>tcflow()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcflush.html"><u><font color=#0000ff>tcflush()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcgetattr.html"><u><font color=#0000ff>tcgetattr()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcgetsid.html"><u><font color=#0000ff>tcgetsid()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcsendbreak.html"><u><font color=#0000ff>tcsendbreak()</font></u></a></em>, <em><a href="http://opengroup.org/onlinepubs/007908799/xsh/tcsetattr.html"><u><font color=#0000ff>tcsetattr()</font></u></a></em>, the <strong>XBD</strong> specification, <a href="http://opengroup.org/onlinepubs/007908799/xbd/termios.html"><u><font color=#800080><strong>General Terminal Interface</strong>&nbsp;</font></u></a>.</blockquote>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25470.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-09 14:38 <a href="http://www.cnitblog.com/zouzheng/articles/25470.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unix编程常见问题解答（9）</title><link>http://www.cnitblog.com/zouzheng/articles/25330.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 08:50:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25330.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25330.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25330.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25330.html</trackback:ping><description><![CDATA[<table style="BORDER-COLLAPSE: collapse" borderColor=#ffd600 height=30 cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr>
            <td>3. 终端输入/输出<br>****************<br><br>3.1 我怎样使我的程序不回射输入？<br>================================<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我怎样能使我的程序不回射输入，就象登录时询问我的口令时那样？<br><br>有一个简单方法，也有一个稍微复杂点的方法：<br><br>简单方法是使用&#8216;getpass()&#8217;函数，它几乎能在所有Unix系统上找到。它以一个<br>给定的字符串参数作为提示符(prompt)。它读取输入直到读到一个&#8216;EOF&#8217;或换<br>行符(译者注：&#8216;EOF&#8217;用&#8216;^d&#8217;输入，而换行符为&#8216;^m&#8217;或回车)然后返回一个<br>指向位于静态内存区包含键入字符的字符串指针。(译者注：字符串不包含换行<br>符)<br><br>复杂一点的方法是使用&#8216;tcgetattr()&#8217;函数和&#8216;tcsetattr()&#8217;函数，两个函数都使用<br>一个&#8216;struct termios&#8217;结构来操纵终端。下面这两段程序应当能设置回射状态和<br>不回射状态。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;stdlib.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;stdio.h&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;termios.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;string.h&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static struct termios stored_settings;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void echo_off(void)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct termios new_settings;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcgetattr(0,&amp;stored_settings);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings = stored_settings;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings.c_lflag &amp;= (~ECHO);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcsetattr(0,TCSANOW,&amp;new_settings);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void echo_on(void)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcsetattr(0,TCSANOW,&amp;stored_settings);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>两段程序使用到的都是在POSIX标准定义的。<br><br>3.2 我怎样从终端读取单个字符？<br>==============================<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我怎样从终端读取单个字符？我的程序总是要等着用户按回车。<br><br>终端通常在标准(canonical)模式，在此模式输入总是经编辑后以行读入。你可以<br>设置终端为非标准(non-canonical)模式，而在此模式下你可以设置在输入传递给<br>你的程序前读入多少字符。你也可以设定非标准模式的计时器为0，这个计时器<br>根据设定的时间间隔清空你的缓冲区。这样做使你可以使用&#8216;getc()&#8217;函数立即<br>获得用户的按键输入。我们使用的&#8216;tcgetattr()&#8217;函数和&#8216;tcsetattr()&#8217;函数都<br>是在POSIX中定义用来操纵&#8216;termios&#8217;结构的。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;stdlib.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;stdio.h&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;termios.h&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#include &lt;string.h&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static struct termios stored_settings;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void set_keypress(void)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct termios new_settings;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcgetattr(0,&amp;stored_settings);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings = stored_settings;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* Disable canonical mode, and set buffer size to 1 byte */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings.c_lflag &amp;= (~ICANON);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings.c_cc[VTIME] = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_settings.c_cc[VMIN] = 1;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcsetattr(0,TCSANOW,&amp;new_settings);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void reset_keypress(void)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tcsetattr(0,TCSANOW,&amp;stored_settings);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>3.3 我怎样检查是否一个键被摁下？<br>================================<br><br>&nbsp;&nbsp;&nbsp;&nbsp;我怎样检查是否一个键被摁下？在DOS上我用&#8216;kbhit()&#8217;函数，但是在UNIX<br>&nbsp;&nbsp;&nbsp;&nbsp;上看来没有相同作用的函数？<br><br>如果你设定了终端为单一字符模式(参见上一个问题解答)，那么(在大多数系统)<br>上你可以使用&#8216;select()&#8217;函数或&#8216;poll()&#8217;函数测试输入是否可读。<br><br>3.4 我怎样将光标在屏幕里移动？<br>==============================<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我怎样将光标在屏幕里移动？我想不用curses而做全屏编辑。(译者注：curses<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;是一个C/C++编程工具库，它提供编程者许多函数调用，在不用关心终端类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;的情况下操纵终端的显示)。<br><br>不开玩笑，你也许不应该想去做这个。Curses工具库知道怎样控制不同终端类型<br>所表现出的奇特的东西(oddities)；当然termcap/terminfo数据会告诉你任何终端类型<br>具有的这些奇特东西，但你可能会发现正确把握所有这些奇特组合是一件艰巨的<br>工作。(译者注：在Linux系统上，termcap数据位于/etc/termcap，而terminfo数据位于<br>/usr/share/terminfo下按不同终端类型首字母存放的不同文件，目前终端类型数已逾<br>两千种)<br><br>但是，你坚决要把你自己搞的手忙脚乱(getting your hands dirty)，那么去研究一下<br>&#8216;termcap&#8217;的函数集，特别是&#8216;tputs()&#8217;，&#8216;tparm()&#8217;和&#8216;tgoto()&#8217;函数。<br><br>3.5 pttys是什么？<br>=================<br><br>Pseudo-teletypes(pttys, ptys，或其它不同的缩写)是具有两部份的伪设备(pseudo-devices)：<br>一部份为&#8220;主人&#8221;一边，你可以认为是一个&#8216;用户&#8217;，另一部份是&#8220;仆人&#8221;一边，<br>它象一个标准的tty设备一样工作。<br><br>它们之所以存在是为了提供在程序控制下的一种模拟串行终端行为的方法。比<br>如，&#8216;telnet&#8217;在远端系统使用一个伪终端；服务器的远端登录shell程序只是从&#8220;仆<br>人&#8221;一边的tty设备期待着得到操作行为，而在&#8220;主人&#8221;一边的伪终端由一个守护程<br>序控制，同时守护程序将所有数据通过网络转发。pttys也被其它程序使用，比如<br>&#8216;xterm&#8217;，&#8216;expect&#8217;，&#8216;script&#8217;，&#8216;screen&#8217;，&#8216;emacs&#8217;和其它很多程序。<br><br>3.6 怎样控制一个串行口和调制解调器？<br>====================================<br><br>Unix系统下对于串行口的控制很大程度上受串行终端传统的使用影响。以往，<br>需要不同ioctls函数调用的组合和其它黑客行为才能控制一个串行设备的正确操<br>作，不过值得庆幸的是，POSIX在这个方面的标准化作了一些努力。<br><br>如果你使用的系统不支持&#8216;&lt;termios.h&gt;&#8217;头文件，&#8216;tcsetattr()&#8217;和其它相关函数，<br>那么你只能到其它地方去找点资料(或将你的老古董系统升级一下)。<br><br>但是不同的系统仍然有显著的区别，主要是在设备名，硬件流控制的操作，和<br>调制解调器的信号方面。(只要可能，尽量让设备驱动程序去做握手(handshaking)<br>工作，而不要试图直接操纵握手信号。)<br><br>打开和初始华串行设备的基本步骤是：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;* 调用&#8216;open()&#8217;函数打开设备；而且可能需要使用特定标志作为参数：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;`O_NONBLOCK'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除非使用这个标志，否则打开一个供拨入(dial-in)或由调制解调器控制的设<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;备会造成&#8216;open()&#8217;调用被阻塞直到线路接通(carrier is present)。一个非<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;阻塞的打开操作给你在需要时令调制解调器控制失效的机会。(参见下面的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CLOCAL)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;`O_NOCTTY'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在自4.4BSD演化的系统上这个标志是多余的，但在其它系统上它控制串行<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设备是否成为会话的控制终端。在大多数情况下你可能不想获得一个控制<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;终端，所以就要设置这个标志，但是也有例外情况。<br><br>&nbsp;&nbsp;&nbsp;* 调用&#8216;tcgetattr()&#8217;函数获得当前设备模式。虽然有人会经常取消(ignore)得到的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大多数或全部初始设定，但它仍然不失为一个初始化&#8216;struct termios&#8217;结构的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;便利方法。<br><br>&nbsp;&nbsp;&nbsp;* 设置termios 结构里&#8216;c_iflag&#8217;，&#8216;c_oflag&#8217;，&#8216;c_flag&#8217;，&#8216;c_lfag&#8217;和&#8216;c_cc&#8217;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为合适的值。(参见下面部分。)<br><br>&nbsp;&nbsp;&nbsp;* 调用&#8216;cfsetispeed()&#8217;和&#8216;cfsetospeed()&#8217;设定设想的波特率。很少系统允许你<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置不同的输入和输出速度，所以普通规律是你需要设成同一个设想的值。<br><br>&nbsp;&nbsp;&nbsp;* 调用&#8216;tcsetattr()&#8217;设定设备模式。<br><br>&nbsp;&nbsp;&nbsp;* 如果你是用&#8216;O_NONBLOCK&#8217;标志打开的端口，你可能希望调用&#8216;fcntl()&#8217;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;函数将&#8216;O_NONBLOCK&#8217;标志重新设置成关闭。因为不同系统看来对是否<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由非阻塞打开的端口对今后的&#8216;read()&#8217;调用造成影响有不同的处理；所以<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最好显式地设置好。<br><br>一旦你打开并设置了端口，你可以正常地调用&#8216;read()&#8217;函数和&#8216;write()&#8217;函数。<br>注意到&#8216;read()&#8217;函数的行为将受到你调用&#8216;tcsetattr()&#8217;函数时的标志参数设定<br>的控制。<br><br>&#8216;tcflush()&#8217;，&#8216;tcdrain()&#8217;，&#8216;tcsendbreak()&#8217;和&#8216;tcflow()&#8217;是其它一些你应当<br>注意的函数。<br><br>当你使用完端口想要关闭时，要注意一些系统上特别恶心的小危险；如果有任何<br>输出数据等待被写到设备(比如输出流被硬件或软件握手而暂停)，你的进程将因<br>为&#8216;close()&#8217;函数调用而被挂起(hang)直到输出数据排空，而且这时的进程是*不<br>可杀的*(unkillably)。所以调用&#8216;tcflush()&#8217;函数丢弃待发数据可能是个明智之举。<br><br>(在我的经验中，在tty设备上被阻塞的输出数据是造成不可杀进程最普通的原因。)<br><br>3.6.1 串行设备和类型<br>--------------------<br><br>不同系统用于串行端口设备的设备名大相径庭。以下是不同系统的一些例子：<br><br>&nbsp;&nbsp;&nbsp;* &#8216;/dev/tty[0-9][a-z]&#8217;作为直接访问设备，而<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8216;/dev/tty[0-9][A-Z]&#8217; 作为调制解调器控制设备(比如SCO Unix)<br><br>&nbsp;&nbsp;&nbsp;* &#8216;/dev/cua[0-9]p[0-9]&#8217;作为直接访问设备，&#8216;/dev/cul[0-9]p[0-9]&#8217;作为拨出设<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;备，而&#8216;/dev/ttyd[0-9]p[0-9]&#8217;作为拨入设备(比如HP-UX)<br><br>&nbsp;&nbsp;&nbsp;* &#8216;/dev/cua[a-z][0-9]&#8217;作为拨出设备而&#8216;/dev/tty[a-z][0-9]&#8217;作为拨入设备(比如<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FreeBSD)<br><br>是否正确地同所用设备名交互，并在任何硬件握手信号线上产生相应的效果是<br>与系统，配置和硬件有关的，但是差不多总是遵循下面这些规则(假设硬件是<br>RS-232 DTE)：<br><br>&nbsp;&nbsp;&nbsp;- 对于任何设备的一个成功打开操作应当设置DTR和RTS<br><br>&nbsp;&nbsp;&nbsp;- 一个对于由调制解调器控制或供拨入的设备的阻塞打开操作将等待DCD(并且<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可能DSR和/或CTS也需要)被设置，通常在设置DTR/RTS之后。<br><br>&nbsp;&nbsp;&nbsp;- 如果一个对于拨出设备的打开操作正巧赶上一个对于相应拨入设备的打开操作<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因为等待线路接通而阻塞，那么打开拨出的操作*也许*造成打开拨入的操作完<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;成，但*也许也不*造成。一些系统为拨入和拨出端口实现一个简单的共享方案，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当拨出端口在使用时，拨入端口被有效地设置成睡眠状态(&#8220;put to sleep&#8221;)；其<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;它系统不这样做，在这种系统上为避免竞争(contention)问题，需要外部的协助才<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;能使拨入和拨出共享端口(比如UUCP锁定文件的使用)。 </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 16:50 <a href="http://www.cnitblog.com/zouzheng/articles/25330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 下串口编程入门</title><link>http://www.cnitblog.com/zouzheng/articles/25329.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 08:46:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25329.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25329.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25329.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25329.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25329.html</trackback:ping><description><![CDATA[<blockquote>Linux 操作系统从一开始就对串行口提供了很好的支持，本文就 Linux 下的串行口通讯编程进行简单的介绍。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=N1003D><span class=atitle>串口简介</span></a></p>
<p>串行口是计算机一种常用的接口，具有连接线少，通讯简单，得到广泛的使用。常用的串口是 RS-232-C 接口（又称 EIA RS-232-C）它是在 1970 年由美国电子工业协会（EIA）联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备（DTE）和数据通讯设备（DCE）之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25 连接器，对连接器的每个引脚的信号内容加以规定，还对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下，传输电缆长度应为 50 英尺。 </p>
<p>Linux 操作系统从一开始就对串行口提供了很好的支持，本文就 Linux 下的串行口通讯编程进行简单的介绍，如果要非常深入了解，建议看看本文所参考的 <a href="http://digilander.libero.it/robang/rubrica/serial.htm" target=_blank><u><font color=#0000ff>《Serial Programming Guide for POSIX Operating Systems》</font></u></a> </p>
<p><strong>计算机串口的引脚说明</strong> </p>
<table cellSpacing=0 cellPadding=0 width="80%" border=1>
    <tbody>
        <tr>
            <td align=middle width="10%">序号</td>
            <td align=middle width="30%">信号名称</td>
            <td align=middle width="10%">符号</td>
            <td align=middle width="10%">流向</td>
            <td align=middle width="40%">功能</td>
        </tr>
        <tr>
            <td align=middle>2</td>
            <td align=middle>发送数据</td>
            <td align=middle>TXD</td>
            <td align=middle>DTE&#8594;DCE</td>
            <td>DTE发送串行数据</td>
        </tr>
        <tr>
            <td align=middle>3</td>
            <td align=middle>接收数据</td>
            <td align=middle>RXD</td>
            <td align=middle>DTE&#8592;DCE</td>
            <td>DTE 接收串行数据</td>
        </tr>
        <tr>
            <td align=middle>4</td>
            <td align=middle>请求发送</td>
            <td align=middle>RTS</td>
            <td align=middle>DTE&#8594;DCE</td>
            <td>DTE 请求 DCE 将线路切换到发送方式</td>
        </tr>
        <tr>
            <td align=middle>5</td>
            <td align=middle>允许发送</td>
            <td align=middle>CTS</td>
            <td align=middle>DTE&#8592;DCE</td>
            <td>DCE 告诉 DTE 线路已接通可以发送数据</td>
        </tr>
        <tr>
            <td align=middle>6</td>
            <td align=middle>数据设备准备好</td>
            <td align=middle>DSR</td>
            <td align=middle>DTE&#8592;DCE</td>
            <td>DCE 准备好</td>
        </tr>
        <tr>
            <td align=middle>7</td>
            <td align=middle>信号地</td>
            <td align=middle>　</td>
            <td align=middle>　</td>
            <td>　　　信号公共地</td>
        </tr>
        <tr>
            <td align=middle>8</td>
            <td align=middle>载波检测</td>
            <td align=middle>DCD</td>
            <td align=middle>DTE&#8592;DCE</td>
            <td>表示 DCE 接收到远程载波</td>
        </tr>
        <tr>
            <td align=middle>20</td>
            <td align=middle>数据终端准备好</td>
            <td align=middle>DTR</td>
            <td align=middle>DTE&#8594;DCE</td>
            <td>DTE 准备好</td>
        </tr>
        <tr>
            <td align=middle>22</td>
            <td align=middle>振铃指示</td>
            <td align=middle>RI</td>
            <td align=middle>DTE&#8592;DCE</td>
            <td>表示 DCE 与线路接通，出现振铃</td>
        </tr>
    </tbody>
</table>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N1013E><span class=atitle>串口操作</span></a></p>
<p>串口操作需要的头文件 </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>#include     &lt;stdio.h&gt;      /*标准输入输出定义*/
            #include     &lt;stdlib.h&gt;     /*标准函数库定义*/
            #include     &lt;unistd.h&gt;     /*Unix 标准函数定义*/
            #include     &lt;sys/types.h&gt;
            #include     &lt;sys/stat.h&gt;
            #include     &lt;fcntl.h&gt;      /*文件控制定义*/
            #include     &lt;termios.h&gt;    /*PPSIX 终端控制定义*/
            #include     &lt;errno.h&gt;      /*错误号定义*/
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N1014B><span class=atitle>打开串口</span></a></p>
<p>在 Linux 下串口文件是位于 /dev 下的 </p>
<p>串口一 为 /dev/ttyS0 </p>
<p>串口二 为 /dev/ttyS1 </p>
<p>打开串口是通过使用标准的文件打开函数操作： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>int fd;
            /*以读写方式打开串口*/
            fd = open( "/dev/ttyS0", O_RDWR);
            if (-1 == fd){
            /* 不能打开串口一*/
            perror(" 提示错误！");
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10161><span class=atitle>设置串口</span></a></p>
<p>最基本的设置串口包括波特率设置，效验位和停止位设置。</p>
<p>串口的设置主要是设置 struct termios 结构体的各成员值。 </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>struct termio
            {	unsigned short  c_iflag;	/* 输入模式标志 */
            unsigned short  c_oflag;		/* 输出模式标志 */
            unsigned short  c_cflag;		/* 控制模式标志*/
            unsigned short  c_lflag;		/* local mode flags */
            unsigned char  c_line;		    /* line discipline */
            unsigned char  c_cc[NCC];    /* control characters */
            };
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>设置这个结构体很复杂，我这里就只说说常见的一些设置： </p>
<p>波特率设置</p>
<p>下面是修改波特率的代码：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>struct  termios Opt;
            tcgetattr(fd, &amp;Opt);
            cfsetispeed(&amp;Opt,B19200);     /*设置为19200Bps*/
            cfsetospeed(&amp;Opt,B19200);
            tcsetattr(fd,TCANOW,&amp;Opt);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>设置波特率的例子函数：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>/**
            *@brief  设置串口通信速率
            *@param  fd     类型 int  打开串口的文件句柄
            *@param  speed  类型 int  串口速度
            *@return  void
            */
            int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
            B38400, B19200, B9600, B4800, B2400, B1200, B300, };
            int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,
            19200,  9600, 4800, 2400, 1200,  300, };
            void set_speed(int fd, int speed){
            int   i;
            int   status;
            struct termios   Opt;
            tcgetattr(fd, &amp;Opt);
            for ( i= 0;  i &lt; sizeof(speed_arr) / sizeof(int);  i++) {
            if  (speed == name_arr[i]) {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&amp;Opt, speed_arr[i]);
            cfsetospeed(&amp;Opt, speed_arr[i]);
            status = tcsetattr(fd1, TCSANOW, &amp;Opt);
            if  (status != 0) {
            perror("tcsetattr fd1");
            return;
            }
            tcflush(fd,TCIOFLUSH);
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>效验位和停止位的设置：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=1>
    <tbody>
        <tr>
            <td align=middle width="20%">无效验</td>
            <td align=middle width="20%">8位</td>
            <td width="60%">Option.c_cflag &amp;= ~PARENB; <br>Option.c_cflag &amp;= ~CSTOPB; <br>Option.c_cflag &amp;= ~CSIZE; <br>Option.c_cflag |= ~CS8; </td>
        </tr>
        <tr>
            <td align=middle width="20%">奇效验(Odd)</td>
            <td align=middle width="20%">7位</td>
            <td width="60%">Option.c_cflag |= ~PARENB; <br>Option.c_cflag &amp;= ~PARODD; <br>Option.c_cflag &amp;= ~CSTOPB; <br>Option.c_cflag &amp;= ~CSIZE; <br>Option.c_cflag |= ~CS7; </td>
        </tr>
        <tr>
            <td align=middle width="20%">偶效验(Even)</td>
            <td align=middle width="20%">7位</td>
            <td width="60%">Option.c_cflag &amp;= ~PARENB; <br>Option.c_cflag |= ~PARODD; <br>Option.c_cflag &amp;= ~CSTOPB; <br>Option.c_cflag &amp;= ~CSIZE; <br>Option.c_cflag |= ~CS7; </td>
        </tr>
        <tr>
            <td align=middle width="20%">Space效验</td>
            <td align=middle width="20%">7位</td>
            <td width="60%">Option.c_cflag &amp;= ~PARENB; <br>Option.c_cflag &amp;= ~CSTOPB; <br>Option.c_cflag &amp;= &amp;~CSIZE; <br>Option.c_cflag |= CS8; </td>
        </tr>
    </tbody>
</table>
<p>设置效验的函数：</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>/**
            *@brief   设置串口数据位，停止位和效验位
            *@param  fd     类型  int  打开的串口文件句柄
            *@param  databits 类型  int 数据位   取值 为 7 或者8
            *@param  stopbits 类型  int 停止位   取值为 1 或者2
            *@param  parity  类型  int  效验类型 取值为N,E,O,,S
            */
            int set_Parity(int fd,int databits,int stopbits,int parity)
            {
            struct termios options;
            if  ( tcgetattr( fd,&amp;options)  !=  0) {
            perror("SetupSerial 1");
            return(FALSE);
            }
            options.c_cflag &amp;= ~CSIZE;
            switch (databits) /*设置数据位数*/
            {
            case 7:
            options.c_cflag |= CS7;
            break;
            case 8:
            options.c_cflag |= CS8;
            break;
            default:
            fprintf(stderr,"Unsupported data size\n"); return (FALSE);
            }
            switch (parity)
            {
            case 'n':
            case 'N':
            options.c_cflag &amp;= ~PARENB;   /* Clear parity enable */
            options.c_iflag &amp;= ~INPCK;     /* Enable parity checking */
            break;
            case 'o':
            case 'O':
            options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
            case 'e':
            case 'E':
            options.c_cflag |= PARENB;     /* Enable parity */
            options.c_cflag &amp;= ~PARODD;   /* 转换为偶效验*/
            options.c_iflag |= INPCK;       /* Disnable parity checking */
            break;
            case 'S':
            case 's':  /*as no parity*/
            options.c_cflag &amp;= ~PARENB;
            options.c_cflag &amp;= ~CSTOPB;break;
            default:
            fprintf(stderr,"Unsupported parity\n");
            return (FALSE);
            }
            /* 设置停止位*/
            switch (stopbits)
            {
            case 1:
            options.c_cflag &amp;= ~CSTOPB;
            break;
            case 2:
            options.c_cflag |= CSTOPB;
            break;
            default:
            fprintf(stderr,"Unsupported stop bits\n");
            return (FALSE);
            }
            /* Set input parity option */
            if (parity != 'n')
            options.c_iflag |= INPCK;
            tcflush(fd,TCIFLUSH);
            options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
            options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
            if (tcsetattr(fd,TCSANOW,&amp;options) != 0)
            {
            perror("SetupSerial 3");
            return (FALSE);
            }
            return (TRUE);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><strong>需要注意的是:</strong> </p>
<p>如果不是开发终端之类的，只是串口传输数据，而不需要串口来处理，那么使用原始模式(Raw Mode)方式来通讯，设置方式如下： </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>options.c_lflag  &amp;= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
            options.c_oflag  &amp;= ~OPOST;   /*Output*/
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10203><span class=atitle>读写串口</span></a></p>
<p>设置好串口之后，读写串口就很容易了，把串口当作文件读写就是。 </p>
<ul>
    <li>发送数据
    <table cellSpacing=0 cellPadding=0 width="100%" border=0>
        <tbody>
            <tr>
                <td class=code-outline>
                <pre class=displaycode>char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length)
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br>
    <li>读取串口数据
    <p>使用文件操作read函数读取，如果设置为原始模式(Raw Mode)传输数据，那么read函数返回的字符数是实际串口收到的字符数。 </p>
    <p>可以使用操作文件的函数来实现异步读取，如fcntl，或者select等来操作。 </p>
    <table cellSpacing=0 cellPadding=0 width="100%" border=0>
        <tbody>
            <tr>
                <td class=code-outline>
                <pre class=displaycode>char  buff[1024];int    Len;int  readByte = read(fd,buff,Len);
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></li>
</ul>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10223><span class=atitle>关闭串口</span></a></p>
<p>关闭串口就是关闭文件。 </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>close(fd);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-serials/index.html#main"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10230><span class=atitle>例子</span></a></p>
<p>下面是一个简单的读取串口数据的例子，使用了上面定义的一些函数和头文件 </p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>/**********************************************************************代码说明：使用串口二测试的，发送的数据是字符，
            但是没有发送字符串结束符号，所以接收到后，后面加上了结束符号。我测试使用的是单片机发送数据到第二个串口，测试通过。
            **********************************************************************/
            #define FALSE  -1
            #define TRUE   0
            /*********************************************************************/
            int OpenDev(char *Dev)
            {
            int	fd = open( Dev, O_RDWR );         //| O_NOCTTY | O_NDELAY
            if (-1 == fd)
            {
            perror("Can't Open Serial Port");
            return -1;
            }
            else
            return fd;
            }
            int main(int argc, char **argv){
            int fd;
            int nread;
            char buff[512];
            char *dev  = "/dev/ttyS1"; //串口二
            fd = OpenDev(dev);
            set_speed(fd,19200);
            if (set_Parity(fd,8,1,'N') == FALSE)  {
            printf("Set Parity Error\n");
            exit (0);
            }
            while (1) //循环读取数据
            {
            while((nread = read(fd, buff, 512))&gt;0)
            {
            printf("\nLen %d\n",nread);
            buff[nread+1] = '\0';
            printf( "\n%s", buff);
            }
            }
            //close(fd);
            // exit (0);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><br>
<p><a name=resources><span class=atitle>参考资料 </span></a></p>
<ul>
    <li><a href="http://digilander.libero.it/robang/rubrica/serial.htm" target=_blank><u><font color=#0000ff>Serial Programming Guide for POSIX Operating Systems</font></u></a> <br><br>
    <li>Linux 的源代码<br><br>
    <li>代码下载: <a href="http://www.ibm.com/developerworks/cn/linux/l-serials/main.c"><u><font color=#0000ff>代码</font></u></a> <br></li>
</ul>
<br><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&#160;</p>
            </td>
            <td><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
            <td width="100%">
            <p>左锦，就职南沙资讯科技园，喜爱 Linux，Java 还有蓝天白云青山绿水。通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#117;&#111;&#49;&#55;&#48;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;&#63;&#99;&#99;&#61;"><u><font color=#0000ff>zuo170@163.com</font></u></a>和他联系。 </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 16:46 <a href="http://www.cnitblog.com/zouzheng/articles/25329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux驱动入门（转）</title><link>http://www.cnitblog.com/zouzheng/articles/25307.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 04:14:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25307.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25307.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25307.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25307.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25307.html</trackback:ping><description><![CDATA[内核版本: 2.4.22 <br>阅读此文的目的: 学会编写Linux设备驱动。 <br>阅读此文的方法: 阅读以下2个文件: hello.c,asdf.c。 <br>此文假设读者: <br>已经能用C语言编写Linux应用程序, <br>理解"字符设备文件, 块设备文件, 主设备号, 次设备号", <br>会写简单的Shell脚本和Makefile。 <br><br>1. "hello.c" <br>-------------------------------- <br>/* <br>* 这是我们的第一个源文件， <br>* 它是一个可以加载的内核模块， <br>* 加载时显示"Hello,World!"， <br>* 卸载时显示"Bye!"。 <br><br>* 需要说明一点，写内核或内核模块不能用写应用程序时的系统调用或函数库， <br>* 因为我们写的就是为应用程序提供系统调用的代码。 <br><br>* 内核有专用的函数库，如&lt;linux/kernel.h&gt;, &lt;linux/fs.h&gt;, &lt;linux/sche.h&gt;等, <br>* 现在还没必要了解得很详细， <br>* 这里用到的printk的功能类似于printf。 <br><br>* "/usr/src/linux"是你实际的内核源码目录的一个符号链接， <br>* 如果没有现在就创建一个,因为下面和以后都会用到。 <br><br>* 编译它用"gcc -c -I/usr/src/linux/include hello.c"， <br>* 如果正常会生成文件hello.o, <br><br>* 加载它用"insmod hello.o", <br>* 只有在文本终端下才能看到输出。 <br><br>* 卸载它用"rmmod hello" <br>*/ <br><br>/* <br>* 小技巧: 在用户目录的.bashrc里加上一行: <br>* alias mkmod='gcc -c -I/usr/src/linux/include' <br>* 然后重新登陆Shell, <br>* 以后就可以用"mkmod hello.c"的方式来编译内核模块了。 <br>*/ <br><br>/* 开始例行公事 */ <br>#ifndef __KERNEL__ <br># define __KERNEL__ <br>#endif <br>#ifndef MODULE <br># define MODULE <br>#endif <br><br>#include &lt;linux/config.h&gt; <br>#include &lt;linux/module.h&gt; <br><br>MODULE_LICENSE("GPL"); <br>#ifdef CONFIG_SMP <br>#define __SMP__ <br>#endif <br>/* 结束例行公事 */ <br><br>#include &lt;linux/kernel.h&gt; /* printk()在这个文件里 */ <br><br>static int <br>init_module <br>(){ <br>printk("Hello,World!\n"); <br>return 0; /* 如果初始工作失败，就返回非0 */ <br>} <br><br>static void <br>cleanup_module <br>(){ <br>printk("Bye!\n"); <br>} <br>------------------------------------ <br><br>2. "asdf.c" <br>------------------------------------ <br>/* <br>* 这个文件是一个内核模块。 <br>* 内核模块的编译，加载和卸载在前面已经介绍了。 <br><br>* 这个模块的功能是，创建一个字符设备。 <br>* 这个设备是一块4096字节的共享内存。 <br>* 内核分配的主设备号会在加载模块时显示。 <br>*/ <br><br>/* 开始例行公事 */ <br>#ifndef __KERNEL__ <br># define __KERNEL__ <br>#endif <br>#ifndef MODULE <br># define MODULE <br>#endif <br><br>#include &lt;linux/config.h&gt; <br>#include &lt;linux/module.h&gt; <br><br>#ifdef CONFIG_SMP <br>#define __SMP__ <br>#endif <br>MODULE_LICENSE("GPL"); <br>/* 结束例行公事 */ <br><br>#include &lt;asm/uaccess.h&gt; /* copy_to_user(), copy_from_user */ <br>#include &lt;linux/fs.h&gt; /* struct file_operations, register_chrdev(), ... */ <br>#include &lt;linux/kernel.h&gt; /* printk()在这个文件里 */ <br>#include &lt;linux/sched.h&gt; /* 和任务调度有关 */ <br>#include &lt;linux/types.h&gt; /* u8, u16, u32 ... */ <br><br>/* <br>* 关于内核功能库，可以去网上搜索详细资料， <br>*/ <br><br>/* 文件被操作时的回调功能 */ <br>static int asdf_open (struct inode *inode, struct file *filp); <br>static int asdf_release (struct inode *inode, struct file *filp); <br>static ssize_t asdf_read (struct file *filp, char *buf, size_t count,loff_t *f_pos); <br>static ssize_t asdf_write (struct file *filp, const char *buf, size_t count,loff_t *f_pos); <br>static loff_t asdf_lseek (struct file * file, loff_t offset, int orig); <br><br>/* 申请主设备号时用的结构, 在linux/fs.h里定义 */ <br>struct file_operations asdf_fops = { <br>open: asdf_open, <br>release: asdf_release, <br>read: asdf_read, <br>write: asdf_write, <br>llseek: asdf_lseek, <br>}; <br><br>static int asdf_major; /* 用来保存申请到的主设备号 */ <br>static u8 asdf_body[4096]="asdf_body\n"; /* 设备 */ <br><br>static int <br>init_module <br>(){ <br>printk ("Hi, This' A Simple Device File!\n"); <br>asdf_major = register_chrdev (0, "A Simple Device File", &amp;asdf_fops); /* 申请字符设备的主设备号 */ <br>if (asdf_major &lt; 0) return asdf_major; /* 申请失败就直接返回错误编号 */ <br>printk ("The major is:%d\n", asdf_major); /* 显示申请到的主设备号 */ <br>return 0; /* 模块正常初始化 */ <br>} <br><br>static void <br>cleanup_module <br>(){ <br>unregister_chrdev(asdf_major, "A Simple Device File"); /* 注销以后,设备就不存在了 */ <br>printk("A Simple Device has been removed,Bye!\n"); <br>} <br><br>/* <br>* 编译这个模块然后加载它, <br>* 如果正常,会显示你的设备的主设备号。 <br>* 现在你的设备就建立好了,我们可以测试一下。 <br>* 假设你的模块申请到的主设备号是254, <br>* 运行"mknod abc c 254 0",就建立了我们的设备文件abc。 <br>* 可以把它当成一个4096字节的内存块来测试一下, <br>* 比如"cat abc", "cp abc image", "cp image abc", <br>* 或写几个应用程序用它来进行通讯。 <br><br>* 介绍一下两个需要注意的事, <br>* 一是printk()的显示只有在非图形模式的终端下才能看到, <br>* 二是加载过的模块最好在不用以后卸载掉。 <br><br>* 如果对Linux环境的系统调用很陌生,建议先看APUE这本书。 <br>*/ <br><br>static int <br>asdf_open /* open回调 */ <br>( <br>struct inode *inode, <br>struct file *filp <br>){ <br>printk("^_^ : open %s\n ",\ <br>current-&gt;comm); <br>/* <br>* 应用程序的运行环境由内核提供,内核的运行环境由硬件提供。 <br>* 这里的current是一个指向当前进程的指针, <br>* 现在没必要了解current的细节。 <br>* 在这里,当前进程正打开这个设备, <br>* 返回0表示打开成功,内核会给它一个文件描述符。 <br>* 这里的comm是当前进程在Shell下的command字符串。 <br>*/ <br>return 0; <br>} <br><br>static int <br>asdf_release /* close回调 */ <br>( <br>struct inode *inode, <br>struct file *filp <br>){ <br>printk("^_^ : close\n "); <br>return 0; <br>} <br><br>static ssize_t <br>asdf_read /* read回调 */ <br>( <br>struct file *filp, <br>char *buf, <br>size_t count, <br>loff_t *f_pos <br>){ <br>loff_t pos; <br>pos = *f_pos; /* 文件的读写位置 */ <br>if ((pos==4096) || (count&gt;4096)) return 0; /* 判断是否已经到设备尾,或写的长度超过设备大小 */ <br>pos += count; <br>if (pos &gt; 4096) { <br>count -= (pos - 4096); <br>pos = 4096; <br>} <br>if (copy_to_user(buf, asdf_body+*f_pos, count)) return -EFAULT; /* 把数据写到应用程序空间 */ <br>*f_pos = pos; /* 改变文件的读写位置 */ <br>return count; /* 返回读到的字节数 */ <br>} <br><br>static ssize_t <br>asdf_write /* write回调,和read一一对应 */ <br>( <br>struct file *filp, <br>const char *buf, <br>size_t count, <br>loff_t *f_pos <br>){ <br>loff_t pos; <br>pos = *f_pos; <br>if ((pos==4096) || (count&gt;4096)) return 0; <br>pos += count; <br>if (pos &gt; 4096) { <br>count -= (pos - 4096); <br>pos = 4096; <br>} <br>if (copy_from_user(asdf_body+*f_pos, buf, count)) return -EFAULT; <br>*f_pos = pos; <br>return count; <br>} <br><br>static loff_t <br>asdf_lseek /* lseek回调 */ <br>( <br>struct file * file, <br>loff_t offset, <br>int orig <br>){ <br>loff_t pos; <br>pos = file-&gt;f_pos; <br>switch (orig) { <br>case 0: <br>pos = offset; <br>break; <br>case 1: <br>pos += offset; <br>break; <br>case 2: <br>pos = 4096+offset; <br>break; <br>default: <br>return -EINVAL; <br>} <br>if ((pos&gt;4096) || (pos&lt;0)) { <br>printk("^_^ : lseek error %d\n",pos); <br>return -EINVAL; <br>} <br>return file-&gt;f_pos = pos; <br>} <br>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 12:14 <a href="http://www.cnitblog.com/zouzheng/articles/25307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>S3c2410的触摸屏及模数转换</title><link>http://www.cnitblog.com/zouzheng/articles/7382.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 08 Mar 2006 12:54:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/7382.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/7382.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/7382.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/7382.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/7382.html</trackback:ping><description><![CDATA[<TABLE height=49 cellSpacing=0 cellPadding=0 width="90%" align=center>
<TBODY>
<TR>
<TD vAlign=top><STRONG>一、<B class=b>触摸屏</B>的几个概念</STRONG><BR>所谓<B class=b>触摸屏</B>，从市场概念来讲，就是一种人人都会使用的计算机输入设备，或者说是人人都会使用的与计算机沟通的设备。不用学习，人人都会使用，是<B class=b>触摸屏</B>最大的魔力，这一点无论是键盘还是鼠标，都无法与其相比。<BR>从技术原理角度讲，<B class=b>触摸屏</B>是一套透明的绝对寻址系统，首先它必须保证是透明的，因此它必须通过材料科技来解决透明问题，像数字化仪、写字板、电梯开关，它们都不是<B class=b>触摸屏</B>；其次它是绝对坐标，手指摸哪就是哪，不需要第二个动作，不像鼠标，是相对定位的一套系统，我们可以注意到，<B class=b>触摸屏</B>软件都不需要游标，有游标反倒影响用户的注意力，因为游标是给相对定位的设备用的，相对定位的设备要移动到一个地方首先要知道现在在何处，往哪个方向去，每时每刻还需要不停的给用户反馈当前的位置才不致于出现偏差。这些对采取绝对坐标定位的<B class=b>触摸屏</B>来说都不需要；再其次就是能检测手指的触摸动作并且判断手指位置，各类<B class=b>触摸屏</B>技术就是围绕“检测手指触摸”而八仙过海各显神通的。 <BR>1、<B class=b>触摸屏</B>的第一个指针：光学特性。它直接影响到<B class=b>触摸屏</B>的视觉效果。但是<B class=b>触摸屏</B>是多层的复合薄膜，光学特性上包括四个方面：透明度、色彩失真度、反光性和清晰度。彩色世界包含了可见光波段中的各种波长色，在没有完全解决透明材料科技之前，或者说还没有低成本的很好解决透明材料科技之前，多层复合薄膜的<B class=b>触摸屏</B>在各波长下的透光性还不能达到理想的一致状态，下面是一个示意图（图6-1）： <BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=189 alt="" src="http://www.dz863.com/myimg/uploadimg/20060101/31.gif" width=376 border=0></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-1<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>由于透光性与波长曲线图的存在，通过<B class=b>触摸屏</B>看到的图像不可避免的与原图像产生了色彩失真，静态的图像感觉还只是色彩的失真，动态的多媒体图像感觉就不是很舒服了，色彩失真度也就是图中的最大色彩失真度自然是越小越好。平常所说的透明度也只能是图中的平均透明度，当然是越高越好。 <BR>反光性，主要是指由于镜面反射造成图像上重迭身后的光影，例如人影、窗户、灯光等。反光是<B class=b>触摸屏</B>带来的负面效果，越小越好，它影响用户的浏览速度，严重时甚至无法辨认图像字符，反光性强的<B class=b>触摸屏</B>使用环境受到限制，现场的灯光布置也被迫需要调整。大多数存在反光问题的<B class=b>触摸屏</B>都提供另外一种经过表面处理的型号：磨砂面<B class=b>触摸屏</B>，也叫防眩型，价格略高一些，防眩型反光性明显下降，适用于采光非常充足的大厅或展览场所，不过，防眩型的透光性和清晰度也随之有较大幅度的下降。 <BR>清晰度，有些<B class=b>触摸屏</B>加装之后，字迹模糊，图像细节模糊，整个屏幕显得模模糊糊，看不太清楚，这就是清晰度太差。清晰度的问题主要是多层薄膜结构的<B class=b>触摸屏</B>，由于薄膜层之间光反复反射折射而造成的，此外防眩型<B class=b>触摸屏</B>由于表面磨砂也会造成清晰度下降。清晰度不好，眼睛容易疲劳，对眼睛也有一定伤害，选购<B class=b>触摸屏</B>时要注意判别。 <BR>2、<B class=b>触摸屏</B>的第二个特性：稳定性。<B class=b>触摸屏</B>是绝对坐标系统，要选哪就直接点那，与鼠标这类相对定位系统的本质区别是一次到位的直观性。绝对坐标系的特点是每一次定位坐标与上一次定位坐标没有关系，<B class=b>触摸屏</B>在物理上是一套独立的坐标定位系统，每次触摸的资料通过校准资料转为屏幕上的坐标，这样，就要求<B class=b>触摸屏</B>这套坐标不管在什幺情况下，同一点的输出资料是稳定的，如果不稳定，那幺这<B class=b>触摸屏</B>就不能保证绝对坐标定位，点不准，这就是<B class=b>触摸屏</B>最怕的问题：漂移。技术原理上凡是不能保证同一点触摸每一次采样资料相同的<B class=b>触摸屏</B>都免不了漂移这个问题，目前有漂移现象的只有电容<B class=b>触摸屏</B>。 <BR>3、<B class=b>触摸屏</B>的第三个特性：相应性。检测触摸并定位，各种<B class=b>触摸屏</B>技术都是依靠各自的传感器来工作的，甚至有的<B class=b>触摸屏</B>本身就是一套传感器。各自的定位原理和各自所用的传感器决定了<B class=b>触摸屏</B>的反应速度、可靠性、稳定性和寿命。<B class=b>触摸屏</B>的传感器方式还决定了<B class=b>触摸屏</B>如何识别多点触摸的问题，也就是超过一点的同时触摸怎幺办？有人触摸时接着旁边又有人触摸怎幺办？这是<B class=b>触摸屏</B>使用过程中经常出现的问题，我认为最理想的方式是：超过一点的同时触摸谁也不判断，一直等到多点触摸移走，有人触摸接着又有人触摸应该是分先后都判断，当然是技术上可能的话。 <BR>红外<B class=b>触摸屏</B>靠多对红外发射和接收对管来工作，红外对管性能和寿命都比较可靠，任何阻挡光线的物体都可用来作触摸物，不过红外<B class=b>触摸屏</B>使用传感器数目将近 100对，并且共享外围电路，这就要求传感器不仅本身性能好，还要求将近100对的红外二极管“光-电阻特性”和“结电容”都保持一致。实际应用中，万一有哪一对出现故障，可以在上电自检过程中发现并在此后加以忽略，靠邻近的红外线代替，由于每一对红外线只“监管”约6mm左右的窄带，而手指通常在15mm左右粗细，用户是察觉不到的。但如果生产过程没有对红外发射管进行老化测试，没有很好的质量管理体系，将近100对的传感器，很快就不是一对两对“掉队”的问题了，总体寿命也就难以保证。下图（图6-2）是红外<B class=b>触摸屏</B>的示意图<BR><BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=282 src="http://www.dz863.com/myimg/uploadimg/20060101/32.gif" width=330></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-2<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<P align=left>电容<B class=b>触摸屏</B>本身实际上是一套精密的漏电传感器，带手套的手不能触摸，由于使用电容方式，导致有漂移现象，在下节电容<B class=b>触摸屏</B>有详细的介绍。超声波<B class=b>触摸屏</B>有表面声波<B class=b>触摸屏</B>和体波声波<B class=b>触摸屏</B>，利用的都是电－声压电换能器作传感器，接收传感器和发射传感器所用的压晶体管不是一种型号，在制造时的掺杂材料略有不同，发射换能器功率大，接收换能器更加灵敏。压电换能器的寿命长，工作稳定，正常工作可以保证10年不出问题。<B class=b>触摸屏</B>安装后，换能器是隐藏起来的，但是在运输和安装过程中需要小心谨慎，裸露的换能器晶体不能碰撞挤压。表面声波<B class=b>触摸屏</B>有X、Y轴两对传感器，利用屏幕表面的声表面波来检测手指触摸，可以说，工作面是一层看不见、打不坏的声能，不怕暴力使用，最适合公共信息查询。 <BR>以上谈了一些<B class=b>触摸屏</B>技术领域的概念，当然，只是是纯技术原理的一些探讨，评判一种<B class=b>触摸屏</B>，光是技术原理还只是其中的一部分，<B class=b>触摸屏</B>要应用到各个领域，还要抵受千触万摸，选用材料的耐用性如何，反应速度如何，价格能否承受，这些都是理性的评判一种<B class=b>触摸屏</B>。 <BR>由于目前基于电阻技术的<B class=b>触摸屏</B>由于价格低廉，亦可满足绝大多数，下面着重介绍一下电阻式<B class=b>触摸屏</B>的基本原理： <BR>电阻<B class=b>触摸屏</B>的屏体部分是一块与显示器表面非常配合的多层复合薄膜，由一层玻璃或有机玻璃作为基层，表面涂有一层透明的导电层，上面再盖有一层外表面硬化处理、光滑防刮的塑料层，它的内表面也涂有一层透明导电层，在两层导电层之间有许多细小（小于千分之一英寸）的透明隔离点把它们隔开绝缘。如图6-3 <BR></P></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=260 src="http://www.dz863.com/myimg/uploadimg/20060101/33.gif" width=214></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-3 电阻<B class=b>触摸屏</B>剖面结构</DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=268 src="http://www.dz863.com/myimg/uploadimg/20060101/34.gif" width=395></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-4</DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=left>当手指<B class=b>触摸屏</B>幕时，平常相互绝缘的两层导电层就在触摸点位置有了一个接触，因其中一面导电层接通Y轴方向的 5V均匀电压场，使得侦测层的电压由零变为非零，控制器侦测到这个接通后，进行A/D转换，并将得到的电压值与5V相比即可得触摸点的Y轴坐标，同理得出X轴的坐标，这就是所有电阻技术<B class=b>触摸屏</B>共同的最基本原理。 <BR>电阻类<B class=b>触摸屏</B>的关键在于材料科技。常用的透明导电涂层材料有： <BR>①ITO，氧化铟，弱导电体，特性是当厚度降到1800个埃（埃＝10-10米）以下时会突然变得透明，透光率为80％，再薄下去透光率反而下降，到 300埃厚度时又上升到80％。ITO是所有电阻技术<B class=b>触摸屏</B>及电容技术<B class=b>触摸屏</B>都用到的主要材料，实际上电阻和电容技术<B class=b>触摸屏</B>的工作面就是ITO涂层。 <BR>②镍金涂层，五线电阻<B class=b>触摸屏</B>的外层导电层使用的是延展性好的镍金涂层材料，外导电层由于频繁触摸，使用延展性好的镍金材料目的是为了延长使用寿命，但是工艺成本较为高昂。镍金导电层虽然延展性好，但是只能作透明导体，不适合作为电阻<B class=b>触摸屏</B>的工作面，因为它导电率高，而且金属不易做到厚度非常均匀，不宜作电压分布层，只能作为探层。 <BR>五线电阻<B class=b>触摸屏</B>：<BR>五线电阻技术<B class=b>触摸屏</B>的基层把两个方向的电压场通过精密电阻网络都加在玻璃的导电工作面上，我们可以简单的理解为两个方向的电压场分时工作加在同一工作面上，而外层镍金导电层只仅仅用来当作纯导体，有触摸后分时检测内层ITO接触点X轴和Y轴电压值的方法测得触摸点的位置。 五线电阻<B class=b>触摸屏</B>内层 ITO需四条引线，外层只作导体仅仅一条，<B class=b>触摸屏</B>得引出线共有5条。五线制电阻<B class=b>触摸屏</B>的结构如图6-5 <BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=168 src="http://www.dz863.com/myimg/uploadimg/20060101/35.gif" width=396></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-5 五线制<B class=b>触摸屏</B>的结构</DIV></TD></TR></TBODY></TABLE>
<TABLE height=49 cellSpacing=0 cellPadding=0 width="90%" align=center><!--DWLayoutTable-->
<TBODY>
<TR>
<TD vAlign=top>四线电阻<B class=b>触摸屏</B>的缺陷：<BR>电阻<B class=b>触摸屏</B>的B面要经常被触动，四线电阻<B class=b>触摸屏</B>的B面采用ITO，我们知道， ITO是极薄的氧化金属，在使用过程中，很快就会产生细小的裂纹，而裂纹一旦产生，原流经该处的电流被迫绕裂纹而行，本该均匀分布的电压随之遭到破坏，<B class=b>触摸屏</B>就有了损伤，表现为裂纹处点不准。 <BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=203 src="http://www.dz863.com/myimg/uploadimg/20060101/36.gif" width=290></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-6 四线制<B class=b>触摸屏</B>的裂纹导致分流<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>随着裂纹的加剧和增多，<B class=b>触摸屏</B>慢慢就会失效，因此使用寿命不长是四线电阻<B class=b>触摸屏</B>的主要问题。<BR>五线电阻<B class=b>触摸屏</B>的改进：<BR>首先五线电阻<B class=b>触摸屏</B>的A面是导电玻璃而不是导电涂覆层，导电玻璃的工艺使得A面的寿命得到极大的提高，并且可以提高透光率。<BR>其次五线电阻<B class=b>触摸屏</B>把工作面的任务都交给寿命长的A面，而B面只用来作为导体，并且采用了延展性好、电阻率低的镍金透明导电层，因此，B面的寿命也极大的提高。<BR>五线电阻<B class=b>触摸屏</B>的另一个专有技术是通过精密的电阻网络来校正A面 的线性问题：由于工艺工程不可避免的有可能厚薄不均而造成电压场不均匀分布，精密电阻网络在工作时流过绝大部分电流，因此可以补偿工作面有可能的线性失真。<BR>五线电阻<B class=b>触摸屏</B>是目前最好的电阻技术<B class=b>触摸屏</B>，最适合于军事、医疗领域使用。 <BR>但是四线电阻<B class=b>触摸屏</B>由于价格低廉，在通用领域的运用，下面将结合S3C2410内置的<B class=b>触摸屏</B>控制器来详细讲解整个<B class=b>触摸屏</B>电路的工作及测量过程。<BR>下图是四线电阻<B class=b>触摸屏</B>测量时的等效电路（图6-7）：<BR><BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=310 src="http://www.dz863.com/myimg/uploadimg/20060101/37.gif" width=252></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-7<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<P align=left>测量时，分为以下3个步骤：<BR>（1） 起初，在<B class=b>触摸屏</B>没有被按下的时候，<B class=b>触摸屏</B>的X轴和Y轴不会接触在一起，此时这个电路处在“Pen Down Detect”状态。S1、S2、S4断开，S3、S5闭合。X+~X-的整个轴上的电压均为0V（GND），Y-端悬空，Y+端由于有上拉电阻R1的存在而呈现高电平。当“Pen Down”后，X轴和Y轴受挤压而接触导通后，Y轴上的电压由于连通到X轴接地而变为低电平，此低电平可做为中断触发信号来通知CPU发生“Pen Down”事件。<BR>（2）当检测到“PenDown”事件后，CPU立刻进入X轴坐标测量状态：S1、S3闭合，S2、S4、S5断开（Y+、Y-两断悬空）。由于X轴和Y轴在接触点按下而连通，因此Y＋端的X_ADC可以认为是X轴的分压采样点（通过测量X_ADC的电压可以得到X+到接触点，以及X-到接触点的比例），从而计算出X轴的坐标<BR>（3） 采样完X轴的坐标后，S1、S3、S5断开，S2、S4闭合，同样原理，我们可以进一步得到Y轴的坐标。<BR></P></TD></TR></TBODY></TABLE><STRONG>二、S3C2410 模数转换器（ADC）及触摸屏控制器<BR></STRONG>S3C2410内置1个8信道的10bit<B class=b>模数转换</B>器（ADC），该ADC能以500KSPS的采样资料将外部的模拟信号转换为10bit分辩率的数字量。同时ADC部分能与CPU的<B class=b>触摸屏</B>控制器协同工作，完成对<B class=b>触摸屏</B>绝对地址的测量。<BR>特性：<BR>－分辩率：10bit<BR>－相信误差：＋/- 2LSB<BR>－最大转换速率：500KSPS<BR>－模拟量输入范围：0～3.3V<BR>－分步 X/Y坐标测量模式<BR>－自动X/Y坐标测量模式<BR>－中断等待模式<BR>下图是ADC及<B class=b>触摸屏</B>控制器部分的逻辑示意图（图6-8）<BR><BR>
<P></P></TD></TR><TR><TD vAlign="top">
<DIV align=center><IMG height=354 src="http://www.dz863.com/myimg/uploadimg/20060101/38.gif" width=516></DIV></TD></TR><TR><TD vAlign="top">
<DIV align=center>图6-8</DIV></TD></TR><TR><TD vAlign="top">随后的图是在S3C2410的ADC以及<B class=b>触摸屏</B>控制器的基础上外接<B class=b>触摸屏</B>的示意图，以及外部电路的实际原理图。需要补充说明的是，图中Q1、Q2为P沟道 MOS管，开门电压为1.8V；Q3、Q4为N沟道MOS管，开门电压为2.7V。运用学过的电子电路的知识，我们知道当MOS管导通后（栅极电压达到开门电压之后），MOS管的源-漏极之间可以认为是直通的（导通电阻为毫欧级），即可以把MOS管认为是图4-7中真正的“开关”。 AVDD 是外部模拟参考源，一般接3.3V电源，XP、XM和YP、YM分别是<B class=b>触摸屏</B>的4条引线，各自对应X轴和Y轴电阻。</TD></TR> <TR><TD vAlign="top">
<DIV align=center><IMG height=317 src="http://www.dz863.com/myimg/uploadimg/20060101/39.gif" width=496></DIV></TD></TR><TR><TD vAlign="top">
<DIV align=center><IMG height=235 src="http://www.dz863.com/myimg/uploadimg/20060101/40.gif" width=392></DIV></TD></TR><TR><TD class=text2 vAlign="center">
<DIV align=center>图6-8</DIV></TD></TR></TBODY></TABLE>
<TABLE height=49 cellSpacing=0 cellPadding=0 width="90%" align=center><!--DWLayoutTable-->
<TBODY>
<TR>
<TD vAlign=top>ADC及<B class=b>触摸屏</B>控制器的工作模式：<BR>1、 ADC普通转换模式（Normal Converson Mode）<BR>普通转换模式（AUTO_PST=0,XY_PST=0）是用来进行一般的ADC转换之用的，例如通过ADC测量电池电压等等。<BR>2、 独立X/Y轴坐标转换模式（Separate X/Y Position Conversion Mode）<BR>独立X/Y轴坐标转换模式其实包含了X轴模式和Y轴模式2种模式。<BR>首先进行X轴的坐标转换（AUTO_PST=0，XY_PST=1），X轴的转换资料会写到ADCDAT0寄存器的XPDAT中，等待转换完成后，<B class=b>触摸屏</B>控制器会产生相应的中断。<BR>然后进行Y轴的坐标转换（AUTO_PST=0，XY_PST=2），Y轴的转换资料会写到ADCDAT1寄存器的YPDAT中，等待转换完成后，<B class=b>触摸屏</B>控制器会产生相应的中断。<BR>3、 自动X/Y轴坐标转换模式（Auto X/Y Position Conversion Mode）<BR>自动X/Y轴坐标转换模式（AUTO_PST=1，XY_PST=0）将会自动地进行X轴和Y轴的转换操作，随后产生相应的中断。<BR>4、 中断等待模式（Wait for InterruptMode）<BR>在系统等待“Pen Down”，即<B class=b>触摸屏</B>按下的时候，其实是处于中断等待模式。一旦被按下，实时产生“INT_TC”中断信号。每次发生此中断都，X轴和Y轴坐标转换资料都可以从相应的资料寄存器中读出。<BR>5、 闲置模式（Standby Mode）<BR>在该模式下转换资料寄存器中的值都被保留为上次转换时的资料。 
<P>ADC及<B class=b>触摸屏</B>控制器的寄存器详解<BR>ADCCON ：ADC控制寄存器（见图6-9）<BR>ENABLE_START ：<BR>置1：启动ADC转换<BR>置0：无操作<BR>RESR_START ：<BR>置1：允许读操作启动ADC转换<BR>置0：禁止读操作启动ADC转换<BR>STDBM：<BR>置1：将ADC置为闲置状态（模式）<BR>置0：将ADC置为正常操作状态<BR>SEL_MUX：选择需要进行转换的ADC信道<BR>PRSCVL：ADC转换时钟预分频参数<BR>PRSCEN：ADC转换时钟使能<BR>ECFLG：ADC转换完成标志位（只读）<BR>为1：ADC转换结束<BR>为0：ADC转换进行中<BR></P><BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=329 src="http://www.dz863.com/myimg/uploadimg/20060101/41.gif" width=430></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-9<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>ADCTSC ：<B class=b>触摸屏</B>控制寄存器（见图6-10）<BR>XY_PST ：对X/Y轴手动测量模式进行选择<BR>AUTO_PST：X/Y轴的自动转换模式使能位<BR>PULL_UP ：XP端的上拉电阻使能位<BR>XP_SEN ：设置nXPON输出状态<BR>XM_SEN ：设置XMON输出状态<BR>YP_SEN ：设置nYPON输出状态<BR>YM_SEN ：设置YMON输出状态<BR><BR></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=350 src="http://www.dz863.com/myimg/uploadimg/20060101/42.gif" width=516></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-10<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<P align=left>ADCDLY ：ADC转换周期等待定时器（见图6-11）</P></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=left>ADCDAT0 ：ADC资料寄存器0（见图6-12）<BR>XPDATA ：X轴转换资料寄存器<BR>XY_PST ：选择X/Y轴自动转换模式<BR>AUTO_PST：X/Y轴自动转换使能位<BR>UPDOWN ：选择中断等待模式的类型<BR>为0：按下产生中断<BR>为1：释放产生中断<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=239 src="http://www.dz863.com/myimg/uploadimg/20060101/44.gif" width=515></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center>图6-12</DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=left>ADCDAT1 : ADC资料寄存器1（见图6-13）<BR>定义类同于ADCDAT0。<BR></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV align=center><IMG height=214 src="http://www.dz863.com/myimg/uploadimg/20060101/45.gif" width=515></DIV></TD></TR>
<TR>
<TD class=text2 vAlign=center>
<DIV align=center>图6-13</DIV></TD></TR></TBODY></TABLE><img src ="http://www.cnitblog.com/zouzheng/aggbug/7382.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-03-08 20:54 <a href="http://www.cnitblog.com/zouzheng/articles/7382.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux操作系统网络驱动程序编写 </title><link>http://www.cnitblog.com/zouzheng/articles/7166.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 04 Mar 2006 01:30:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/7166.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/7166.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/7166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/7166.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/7166.html</trackback:ping><description><![CDATA[<DIV class=postText>Linux操作系统网络驱动程序编写 <BR><BR>一.Linux系统设备驱动程序概述 <BR>1.1 Linux设备驱动程序分类 <BR>1.2 编写驱动程序的一些基本概念 <BR>二.Linux系统网络设备驱动程序 <BR>2.1 网络驱动程序的结构 <BR>2.2 网络驱动程序的基本方法 <BR>2.3 网络驱动程序中用到的数据结构 <BR>2.4 常用的系统支持 <BR>三.编写Linux网络驱动程序中可能遇到的问题 <BR>3.1 中断共享 <BR>3.2 硬件发送忙时的处理 <BR>3.3 流量控制(flow control) <BR>3.4 调试 <BR>四.进一步的阅读 <BR>&nbsp;<BR><BR><BR>一.Linux系统设备驱动程序概述 <BR>1.1 Linux设备驱动程序分类 <BR>Linux设备驱动程序在Linux的内核源代码中占有很大的比例，源代码的长度日 <BR>益增加，主要是驱动程序的增加。在Linux内核的不断升级过程中，驱动程序的结构 <BR>还是相对稳定。在2.0.xx到2.2.xx的变动里，驱动程序的编写做了一些改变，但是 <BR>从2.0.xx的驱动到2.2.xx的移植只需做少量的工作。 <BR><BR>Linux系统的设备分为字符设备(char device)，块设备(block device)和网络 <BR>设备(network device)三种。字符设备是指存取时没有缓存的设备。块设备的读写 <BR>都有缓存来支持，并且块设备必须能够随机存取(random access)，字符设备则没有 <BR>这个要求。典型的字符设备包括鼠标，键盘，串行口等。块设备主要包括硬盘软盘 <BR>设备，CD-ROM等。一个文件系统要安装进入操作系统必须在块设备上。 <BR><BR>网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket <BR>机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系 <BR>统里支持对发送数据和接收数据的缓存，提供流量控制机制，提供对多协议的支持。 <BR><BR><BR>1.2 编写驱动程序的一些基本概念 <BR>无论是什么操作系统的驱动程序，都有一些通用的概念。操作系统提供给驱动 <BR>程序的支持也大致相同。下面简单介绍一下网络设备驱动程序的一些基本要求。 <BR><BR>1.2.1 发送和接收 <BR>这是一个网络设备最基本的功能。一块网卡所做的无非就是收发工作。所以驱 <BR>动程序里要告诉系统你的发送函数在哪里，系统在有数据要发送时就会调用你的发 <BR>送程序。还有驱动程序由于是直接操纵硬件的，所以网络硬件有数据收到最先能得 <BR>到这个数据的也就是驱动程序，它负责把这些原始数据进行必要的处理然后送给系 <BR>统。这里，操作系统必须要提供两个机制，一个是找到驱动程序的发送函数，一个 <BR>是驱动程序把收到的数据送给系统。 <BR><BR>1.2.2 中断 <BR>中断在现代计算机结构中有重要的地位。操作系统必须提供驱动程序响应中断 <BR>的能力。一般是把一个中断处理程序注册到系统中去。操作系统在硬件中断发生后 <BR>调用驱动程序的处理程序。Linux支持中断的共享，即多个设备共享一个中断。 <BR><BR>1.2.3 时钟 <BR>在实现驱动程序时，很多地方会用到时钟。如某些协议里的超时处理，没有中 <BR>断机制的硬件的轮询等。操作系统应为驱动程序提供定时机制。一般是在预定的时 <BR>间过了以后回调注册的时钟函数。在网络驱动程序中，如果硬件没有中断功能，定 <BR>时器可以提供轮询(poll)方式对硬件进行存取。或者是实现某些协议时需要的超时 <BR>重传等。 <BR><BR><BR>二.Linux系统网络设备驱动程序 <BR><BR>2.1 网络驱动程序的结构 <BR>所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。 <BR>一个设备就是一个对象(device 结构)，它内部有自己的数据和方法。每一个设备的 <BR>方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身 <BR>的数据(类似面向对象程序设计时的this引用)。 <BR>一个网络设备最基本的方法有初始化、发送和接收。 <BR><BR>------------------- --------------------- <BR>|deliver packets | |receive packets queue| <BR>|(dev_queue_xmit()) | |them(netif_rx()) | <BR>------------------- --------------------- <BR>| | / \ <BR>\ / | | <BR>------------------------------------------------------- <BR>| methods and variables(initialize,open,close,hard_xmit,| <BR>| interrupt handler,config,resources,status...) | <BR>------------------------------------------------------- <BR>| | / \ <BR>\ / | | <BR>----------------- ---------------------- <BR>|send to hardware | |receivce from hardware| <BR>----------------- ---------------------- <BR>| | / \ <BR>\ / | | <BR>----------------------------------------------------- <BR>| hardware media | <BR>----------------------------------------------------- <BR><BR>初始化程序完成硬件的初始化、device中变量的初始化和系统资源的申请。发送 <BR>程序是在驱动程序的上层协议层有数据要发送时自动调用的。一般驱动程序中不对发 <BR>送数据进行缓存，而是直接使用硬件的发送功能把数据发送出去。接收数据一般是通 <BR>过硬件中断来通知的。在中断处理程序里，把硬件帧信息填入一个skbuff结构中，然 <BR>后调用netif_rx()传递给上层处理。 <BR><BR><BR>2.2 网络驱动程序的基本方法 <BR>网络设备做为一个对象，提供一些方法供系统访问。正是这些有统一接口的方法， <BR>掩蔽了硬件的具体细节，让系统对各种网络设备的访问都采用统一的形式，做到硬件 <BR>无关性。 <BR>下面解释最基本的方法。 <BR>2.2.1 初始化(initialize) <BR>驱动程序必须有一个初始化方法。在把驱动程序载入系统的时候会调用这个初 <BR>始化程序。它做以下几方面的工作。检测设备。在初始化程序里你可以根据硬件的 <BR>特征检查硬件是否存在，然后决定是否启动这个驱动程序。配置和初始化硬件。在 <BR>初始化程序里你可以完成对硬件资源的配置，比如即插即用的硬件就可以在这个时 <BR>候进行配置(Linux内核对PnP功能没有很好的支持，可以在驱动程序里完成这个功 <BR>能)。配置或协商好硬件占用的资源以后，就可以向系统申请这些资源。有些资源是 <BR>可以和别的设备共享的，如中断。有些是不能共享的，如IO、DMA。接下来你要初始 <BR>化device结构中的变量。最后，你可以让硬件正式开始工作。 <BR><BR>2.2.2 打开(open) <BR>open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状 <BR>态由down--&gt;up)。所以实际上很多在initialize中的工作可以放到这里来做。比如资 <BR>源的申请，硬件的激活。如果dev-&gt;open返回非0(error)，则硬件的状态还是down。 <BR>Open方法另一个作用是如果驱动程序做为一个模块被装入，则要防止模块卸载时 <BR>设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。 <BR><BR>2.2.3 关闭(stop) <BR>close方法做和open相反的工作。可以释放某些资源以减少系统负担。Close是在 <BR>设备状态由up转为down时被调用的。另外如果是做为模块装入的驱动程序，close里 <BR>应该调用MOD_DEC_USE_COUNT，减少设备被引用的次数，以使驱动程序可以被卸载。 <BR>另外close方法必须返回成功(0==success)。 <BR><BR>2.2.4 发送(hard_start_xmit) <BR>所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit <BR>时，发送的数据放在一个sk_buff结构中。一般的驱动程序把数据传给硬件发出去。 <BR>也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统，或者 <BR>dummy设备直接丢弃数据。 <BR>如果发送成功，hard_start_xmit方法里释放sk_buff，返回0(发送成功)。如果 <BR>设备暂时无法处理，比如硬件忙，则返回1。这时如果dev-&gt;tbusy置为非0，则系统 <BR>认为硬件忙，要等到dev-&gt;tbusy置0以后才会再次发送。Tbusy的置0任务一般由中断 <BR>完成。硬件在发送结束后产生中断，这时可以把tbusy置0，然后用mark_bh()调用通 <BR>知系统可以再次发送。在发送不成功的情况下，也可以不置dev-&gt;tbusy为非0，这样 <BR>系统会不断尝试重发。如果hard_start_xmit发送不成功，则不要释放sk_buff。 <BR>传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送方法里不需 <BR>要再填充硬件帧头，数据可以直接提交给硬件发送。Sk_buff是被锁住的(locked)， <BR>确保其他程序不会存取它。 <BR><BR>2.2.5 接收(reception) <BR>驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。 <BR>一般设备收到数据后都会产生一个中断，在中断处理程序中驱动程序申请一块 <BR>sk_buff(skb)，从硬件读出数据放置到申请好的缓冲区里。接下来填充sk_buff中 <BR>的一些信息。Skb-&gt;dev = dev，判断收到帧的协议类型，填入skb-&gt;protocol(多协 <BR>议的支持)。把指针skb-&gt;mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要 <BR>设置skb-&gt;pkt_type，标明第二层(链路层)数据类型。可以是以下类型： <BR>PACKET_BROADCAST : 链路层广播 <BR>PACKET_MULTICAST : 链路层组播 <BR>PACKET_SELF : 发给自己的帧 <BR>PACKET_OTHERHOST : 发给别人的帧(监听模式时会有这种帧) <BR><BR>最后调用netif_rx()把数据传送给协议层。Netif_rx()里数据放入处理队列然后返 <BR>回，真正的处理是在中断返回以后，这样可以减少中断时间。调用netif_rx()以后， <BR>驱动程序就不能再存取数据缓冲区skb。 <BR><BR>2.2.6 硬件帧头(hard_header) <BR>硬件一般都会在上层数据发送之前加上自己的硬件帧头，比如以太网(Ethernet) <BR>就有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的前面的。驱动程序提供 <BR>一个hard_header方法，协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。 <BR>硬件帧头的长度必须填在dev-&gt;hard_header_len，这样协议层回在数据之前保留好 <BR>硬件帧头的空间。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就 <BR>可以了。 <BR>在协议层调用hard_header时，传送的参数包括(2.0.xx)：数据的sk_buff， <BR>device指针，protocol，目的地址(daddr)，源地址(saddr)，数据长度(len)。数据 <BR>长度不要使用sk_buff中的参数，因为调用hard_header时数据可能还没完全组织好。 <BR>Saddr是NULL的话是使用缺省地址(default)。Daddr是NULL表明协议层不知道硬件目 <BR>的地址。如果hard_header完全填好了硬件帧头，则返回添加的字节数。如果硬件帧 <BR>头中的信息还不完全(比如daddr为NULL，但是帧头中需要目的硬件地址。典型的情 <BR>况是以太网需要地址解析(arp))，则返回负字节数。Hard_header返回负数的情况 <BR>下，协议层会做进一步的build header的工作。目前Linux系统里就是做arp <BR>(如果hard_header返回正，dev-&gt;arp=1，表明不需要做arp，返回负，dev-&gt;arp=0， <BR>做arp)。 <BR>对hard_header的调用在每个协议层的处理程序里。如ip_output。 <BR><BR>2.2.7 地址解析(xarp) <BR>有些网络有硬件地址(比如Ethernet)，并且在发送硬件帧时需要知道目的硬件 <BR>地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。这个对应是通过地址 <BR>解析完成的。需要做arp的的设备在发送之前会调用驱动程序的rebuild_header方 <BR>法。调用的主要参数包括指向硬件帧头的指针，协议层地址。如果驱动程序能够解 <BR>析硬件地址，就返回1，如果不能，返回0。 <BR>对rebuild_header的调用在net/core/dev.c的do_dev_queue_xmit()里。 <BR><BR>2.2.8 参数设置和统计数据 <BR>在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般 <BR>只有超级用户(root)权限才能对设备参数进行设置。设置方法有： <BR>dev-&gt;set_mac_address() <BR>当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般 <BR>对mac地址的设置没有太大意义的。 <BR>Dev-&gt;set_config() <BR><BR>当用户调用ioctl时类型为SIOCSIFMAP时，系统会调用驱动程序的set_config <BR>方法。用户会传递一个ifmap结构包含需要的I/O、中断等参数。 <BR>Dev-&gt;do_ioctl() <BR>如果用户调用ioctl时类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间，系统 <BR>会调用驱动程序的这个方法。一般是设置设备的专用数据。 <BR>读取信息也是通过ioctl调用进行。除次之外驱动程序还可以提供一个 <BR>dev-&gt;get_stats方法，返回一个enet_statistics结构，包含发送接收的统计信息。 <BR>Ioctl的处理在net/core/dev.c的dev_ioctl()和dev_ifsioc()里。 <BR><BR><BR>2.3 网络驱动程序中用到的数据结构 <BR>最重要的是网络设备的数据结构。定义在include/linux/netdevice.h里。它 <BR>的注释已经足够详尽。 <BR>Struct device <BR>{ <BR><BR>/* <BR>* This is the first field of the "visible" part of this structure <BR>* (I.e. As seen by users in the "Space.c" file). It is the name <BR>* the interface. <BR>*/ <BR>char *name; <BR><BR>/* I/O specific fields - FIXME: Merge these and struct ifmap into one */ <BR>unsigned long rmem_end; /* shmem "recv" end */ <BR>unsigned long rmem_start; /* shmem "recv" start */ <BR>unsigned long mem_end; /* shared mem end */ <BR>unsigned long mem_start; /* shared mem start */ <BR>unsigned long base_addr; /* device I/O address */ <BR>unsigned char irq; /* device IRQ number */ <BR><BR>/* Low-level status flags. */ <BR>volatile unsigned char start, /* start an operation */ <BR>interrupt; /* interrupt arrived */ <BR>/* 在处理中断时interrupt设为1，处理完清0。 */ <BR>unsigned long tbusy; /* transmitter busy must be long <BR>for <BR>bitops */ <BR><BR>struct device *next; <BR><BR>/* The device initialization function. Called only once. */ <BR>/* 指向驱动程序的初始化方法。 */ <BR>int (*init)(struct device *dev); <BR><BR>/* Some hardware also needs these fields, but they are not part of the <BR>usual set specified in Space.c. */ <BR>/* 一些硬件可以在一块板上支持多个接口，可能用到if_port。 */ <BR>unsigned char if_port; /* Selectable AUI, TP,..*/ <BR>unsigned char dma; /* DMA channel */ <BR><BR>struct enet_statistics* (*get_stats)(struct device *dev); <BR><BR>/* <BR>* This marks the end of the "visible" part of the structure. All <BR>* fields hereafter are internal to the system, and may change at <BR>* will (read: may be cleaned up at will). <BR>*/ <BR><BR>/* These may be needed for future network-power-down code. */ <BR>/* trans_start记录最后一次成功发送的时间。可以用来确定硬件是否工作正常。*/ <BR>unsigned long trans_start; /* Time (in jiffies) of last Tx */ <BR>unsigned long last_rx; /* Time of last Rx */ <BR><BR>/* flags里面有很多内容，定义在include/linux/if.h里。*/ <BR>unsigned short flags; /* interface flags (a la BSD) */ <BR>unsigned short family; /* address family ID (AF_INET) */ <BR>unsigned short metric; /* routing metric (not used) */ <BR>unsigned short mtu; /* interface MTU value */ <BR><BR>/* type标明物理硬件的类型。主要说明硬件是否需要arp。定义在 <BR>include/linux/if_arp.h里。 */ <BR>unsigned short type; /* interface hardware type */ <BR><BR>/* 上层协议层根据hard_header_len在发送数据缓冲区前面预留硬件帧头空间。*/ <BR>unsigned short hard_header_len; /* hardware hdr length */ <BR><BR>/* priv指向驱动程序自己定义的一些参数。*/ <BR>void *priv; /* pointer to private data */ <BR><BR>/* Interface address info. */ <BR>unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ <BR>unsigned char pad; /* make dev_addr aligned <BR>to 8 <BR>bytes */ <BR>unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */ <BR>unsigned char addr_len; /* hardware address length */ <BR>unsigned long pa_addr; /* protocol address */ <BR>unsigned long pa_brdaddr; /* protocol broadcast addr */ <BR>unsigned long pa_dstaddr; /* protocol P-P other side addr */ <BR>unsigned long pa_mask; /* protocol netmask */ <BR>unsigned short pa_alen; /* protocol address length */ <BR><BR>struct dev_mc_list *mc_list; /* Multicast mac addresses */ <BR>int mc_count; /* Number of installed mcasts */ <BR><BR>struct ip_mc_list *ip_mc_list; /* IP multicast filter chain */ <BR>__u32 tx_queue_len; /* Max frames per queue allowed */ <BR><BR>/* For load balancing driver pair support */ <BR><BR>unsigned long pkt_queue; /* Packets queued */ <BR>struct device *slave; /* Slave device */ <BR>struct net_alias_info *alias_info; /* main dev alias info */ <BR>struct net_alias *my_alias; /* alias devs */ <BR><BR>/* Pointer to the interface buffers. */ <BR>struct sk_buff_head buffs[DEV_NUMBUFFS]; <BR><BR>/* Pointers to interface service routines. */ <BR>int (*open)(struct device *dev); <BR>int (*stop)(struct device *dev); <BR>int (*hard_start_xmit) (struct sk_buff *skb, <BR>struct device *dev); <BR>int (*hard_header) (struct sk_buff *skb, <BR>struct device *dev, <BR>unsigned short type, <BR>void *daddr, <BR>void *saddr, <BR>unsigned len); <BR>int (*rebuild_header)(void *eth, struct device *dev, <BR>unsigned long raddr, struct sk_buff *skb); <BR>#define HAVE_MULTICAST <BR>void (*set_multicast_list)(struct device *dev); <BR>#define HAVE_SET_MAC_ADDR <BR>int (*set_mac_address)(struct device *dev, void *addr); <BR>#define HAVE_PRIVATE_IOCTL <BR>int (*do_ioctl)(struct device *dev, struct ifreq *ifr, int <BR>cmd); <BR>#define HAVE_SET_CONFIG <BR>int (*set_config)(struct device *dev, struct ifmap *map); <BR>#define HAVE_HEADER_CACHE <BR>void (*header_cache_bind)(struct hh_cache **hhp, struct dev <BR>ice <BR>*dev, unsigned short htype, __u32 daddr); <BR>void (*header_cache_update)(struct hh_cache *hh, struct dev <BR>ice <BR>*dev, unsigned char * haddr); <BR>#define HAVE_CHANGE_MTU <BR>int (*change_mtu)(struct device *dev, int new_mtu); <BR><BR>struct iw_statistics* (*get_wireless_stats)(struct device *dev); <BR>}; <BR><BR><BR>2.4 常用的系统支持 <BR><BR>2.4.1 内存申请和释放 <BR>include/linux/kernel.h里声明了kmalloc()和kfree()。用于在内核模式下申 <BR>请和释放内存。 <BR>Void *kmalloc(unsigned int len,int priority); <BR>void kfree(void *__ptr); <BR>与用户模式下的malloc()不同，kmalloc()申请空间有大小限制。长度是2的整 <BR>次方。可以申请的最大长度也有限制。另外kmalloc()有priority参数，通常使用 <BR>时可以为GFP_KERNEL，如果在中断里调用用GFP_ATOMIC参数，因为使用GFP_KERNEL <BR>则调用者可能进入sleep状态，在处理中断时是不允许的。 <BR>Kfree()释放的内存必须是kmalloc()申请的。如果知道内存的大小，也可以用 <BR>kfree_s()释放。 <BR><BR>2.4.2 request_irq()、free_irq() <BR>这是驱动程序申请中断和释放中断的调用。在include/linux/sched.h里声明。 <BR>Request_irq()调用的定义： <BR>int request_irq(unsigned int irq, <BR>void (*handler)(int irq, void *dev_id, struct pt_regs *regs), <BR>unsigned long irqflags, <BR>const char * devname, <BR>void *dev_id); <BR>irq是要申请的硬件中断号。在Intel平台，范围0--15。Handler是向系统登记 <BR>的中断处理函数。这是一个回调函数，中断发生时，系统调用这个函数，传入的参 <BR>数包括硬件中断号，device id，寄存器值。Dev_id就是下面的request_irq时传递 <BR>给系统的参数dev_id。Irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT， <BR>标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置 <BR>SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有 <BR>一个SA_SHIRQ属性，设置了以后运行多个设备共享中断。Dev_id在中断共享时会用 <BR>到。一般设置为这个设备的device结构本身或者NULL。中断处理程序可以用dev_id <BR>找到相应的控制这个中断的设备，或者用irq2dev_map找到中断对应的设备。 <BR>Void free_irq(unsigned int irq,void *dev_id); <BR><BR>2.4.3 时钟 <BR>时钟的处理类似中断，也是登记一个时间处理函数，在预定的时间过后，系统 <BR>会调用这个函数。在include/linux/timer.h里声明。 <BR>Struct timer_list { <BR>struct timer_list *next; <BR>struct timer_list *prev; <BR>unsigned long expires; <BR>unsigned long data; <BR>void (*function)(unsigned long); <BR>}; <BR>void add_timer(struct timer_list * timer); <BR>int del_timer(struct timer_list * timer); <BR>void init_timer(struct timer_list * timer); <BR>使用时钟，先声明一个timer_list结构，调用init_timer对它进行初始化。 <BR>Time_list结构里expires是标明这个时钟的周期，单位采用jiffies的单位。 <BR>Jiffies是Linux一个全局变量，代表时间。它的单位随硬件平台的不同而不同。 <BR>系统里定义了一个常数HZ，代表每秒种最小时间间隔的数目。这样jiffies的单位 <BR>就是1/HZ。Intel平台jiffies的单位是1/100秒，这就是系统所能分辨的最小时间 <BR>间隔了。所以expires/HZ就是以秒为单位的这个时钟的周期。 <BR>Function就是时间到了以后的回调函数，它的参数就是timer_list中的data。 <BR>Data这个参数在初始化时钟的时候赋值，一般赋给它设备的device结构指针。 <BR>在预置时间到系统调用function，同时系统把这个time_list从定时队列里清 <BR>除。所以如果需要一直使用定时函数，要在function里再次调用add_timer()把这 <BR>个timer_list加进定时队列。 <BR><BR>2.4.4 I/O <BR>I/O端口的存取使用： <BR>inline unsigned int inb(unsigned short port); <BR>inline unsigned int inb_p(unsigned short port); <BR>inline void outb(char value, unsigned short port); <BR>inline void outb_p(char value, unsigned short port); <BR>在include/adm/io.h里定义。 <BR>Inb_p()、outb_p()与inb()、outb_p()的不同在于前者在存取I/O时有等待 <BR>(pause)一适应慢速的I/O设备。 <BR>为了防止存取I/O时发生冲突，Linux提供对端口使用情况的控制。在使用端口 <BR>之前，可以检查需要的I/O是否正在被使用，如果没有，则把端口标记为正在使用， <BR>使用完后再释放。系统提供以下几个函数做这些工作。 <BR>Int check_region(unsigned int from, unsigned int extent); <BR>void request_region(unsigned int from, unsigned int extent,const char *name) <BR>; <BR>void release_region(unsigned int from, unsigned int extent); <BR>其中的参数from表示用到的I/O端口的起始地址，extent标明从from开始的端 <BR>口数目。Name为设备名称。 <BR><BR>2.4.5 中断打开关闭 <BR>系统提供给驱动程序开放和关闭响应中断的能力。是在include/asm/system.h <BR>中的两个定义。 <BR>#define cli() __asm__ __volatile__ ("cli"::) <BR>#define sti() __asm__ __volatile__ ("sti"::) <BR><BR>2.4.6 打印信息 <BR>类似普通程序里的printf()，驱动程序要输出信息使用printk()。在include <BR>/linux/kernel.h里声明。 <BR>Int printk(const char* fmt, ...); <BR>其中fmt是格式化字符串。...是参数。都是和printf()格式一样的。 <BR><BR>2.4.7 注册驱动程序 <BR>如果使用模块(module)方式加载驱动程序，需要在模块初始化时把设备注册 <BR>到系统设备表里去。不再使用时，把设备从系统中卸除。定义在drivers/net/net_init.h <BR>里的两个函数完成这个工作。 <BR>Int register_netdev(struct device *dev); <BR>void unregister_netdev(struct device *dev); <BR>dev就是要注册进系统的设备结构指针。在register_netdev()时，dev结构一 <BR>般填写前面11项，即到init，后面的暂时可以不用初始化。最重要的是name指针和 <BR>init方法。Name指针空(NULL)或者内容为'\0'或者name[0]为空格(space)，则系统 <BR>把你的设备做为以太网设备处理。以太网设备有统一的命名格式，ethX。对以太网 <BR>这么特别对待大概和Linux的历史有关。 <BR>Init方法一定要提供，register_netdev()会调用这个方法让你对硬件检测和 <BR>设置。 <BR>Register_netdev()返回0表示成功，非0不成功。 <BR><BR>2.4.8 sk_buff <BR>Linux网络各层之间的数据传送都是通过sk_buff。Sk_buff提供一套管理缓冲 <BR>区的方法，是Linux系统网络高效运行的关键。每个sk_buff包括一些控制方法和一 <BR>块数据缓冲区。控制方法按功能分为两种类型。一种是控制整个buffer链的方法， <BR>另一种是控制数据缓冲区的方法。Sk_buff组织成双向链表的形式，根据网络应用 <BR>的特点，对链表的操作主要是删除链表头的元素和添加到链表尾。Sk_buff的控制 <BR>方法都很短小以尽量减少系统负荷。(translated from article written by Alan <BR>Cox) <BR>常用的方法包括： <BR>.alloc_skb() 申请一个sk_buff并对它初始化。返回就是申请到的sk_buff。 <BR>.dev_alloc_skb()类似alloc_skb，在申请好缓冲区后，保留16字节的帧头空 <BR>间。主要用在Ethernet驱动程序。 <BR>.kfree_skb() 释放一个sk_buff。 <BR>.skb_clone() 复制一个sk_buff，但不复制数据部分。 <BR>.skb_copy()完全复制一个sk_buff。 <BR>.skb_dequeue() 从一个sk_buff链表里取出第一个元素。返回取出的sk_buff， <BR>如果链表空则返回NULL。这是常用的一个操作。 <BR>.skb_queue_head() 在一个sk_buff链表头放入一个元素。 <BR>.skb_queue_tail() 在一个sk_buff链表尾放入一个元素。这也是常用的一个 <BR>操作。网络数据的处理主要是对一个先进先出队列的管理，skb_queue_tail() <BR>和skb_dequeue()完成这个工作。 <BR>.skb_insert() 在链表的某个元素前插入一个元素。 <BR>.skb_append() 在链表的某个元素后插入一个元素。一些协议(如TCP)对没按 <BR>顺序到达的数据进行重组时用到skb_insert()和skb_append()。 <BR><BR>.skb_reserve() 在一个申请好的sk_buff的缓冲区里保留一块空间。这个空间 <BR>一般是用做下一层协议的头空间的。 <BR>.skb_put() 在一个申请好的sk_buff的缓冲区里为数据保留一块空间。在 <BR>alloc_skb以后，申请到的sk_buff的缓冲区都是处于空(free)状态，有一个 <BR>tail指针指向free空间，实际上开始时tail就指向缓冲区头。Skb_reserve() <BR>在free空间里申请协议头空间，skb_put()申请数据空间。见下面的图。 <BR>.skb_push() 把sk_buff缓冲区里数据空间往前移。即把Head room中的空间移 <BR>一部分到Data area。 <BR>.skb_pull() 把sk_buff缓冲区里Data area中的空间移一部分到Head room中。 <BR><BR>-------------------------------------------------- <BR>| Tail room(free) | <BR>-------------------------------------------------- <BR>After alloc_skb() <BR><BR>-------------------------------------------------- <BR>| Head room | Tail room(free) | <BR>-------------------------------------------------- <BR>After skb_reserve() <BR><BR>-------------------------------------------------- <BR>| Head room | Data area | Tail room(free) | <BR>-------------------------------------------------- <BR>After skb_put() <BR><BR>-------------------------------------------------- <BR>|Head| skb_ | Data | Tail room(free) | <BR>|room| push | | | <BR>| | Data area | | <BR>-------------------------------------------------- <BR>After skb_push() <BR><BR>-------------------------------------------------- <BR>| Head | skb_ | Data area | Tail room(free) | <BR>| | pull | | | <BR>| Head room | | | <BR>-------------------------------------------------- <BR>After skb_pull() <BR><BR><BR>三.编写Linux网络驱动程序中需要注意的问题 <BR><BR>3.1 中断共享 <BR>Linux系统运行几个设备共享同一个中断。需要共享的话，在申请的时候指明 <BR>共享方式。系统提供的request_irq()调用的定义： <BR>int request_irq(unsigned int irq, <BR>void (*handler)(int irq, void *dev_id, struct pt_regs *regs), <BR>unsigned long irqflags, <BR>const char * devname, <BR>void *dev_id); <BR>如果共享中断，irqflags设置SA_SHIRQ属性，这样就允许别的设备申请同一个 <BR>中断。需要注意所有用到这个中断的设备在调用request_irq()都必须设置这个属 <BR>性。系统在回调每个中断处理程序时，可以用dev_id这个参数找到相应的设备。一 <BR>般dev_id就设为device结构本身。系统处理共享中断是用各自的dev_id参数依次调 <BR>用每一个中断处理程序。 <BR><BR>3.2 硬件发送忙时的处理 <BR>主CPU的处理能力一般比网络发送要快，所以经常会遇到系统有数据要发，但 <BR>上一包数据网络设备还没发送完。因为在Linux里网络设备驱动程序一般不做数据 <BR>缓存，不能发送的数据都是通知系统发送不成功，所以必须要有一个机制在硬件不 <BR>忙时及时通知系统接着发送下面的数据。 <BR>一般对发送忙的处理在前面设备的发送方法(hard_start_xmit)里已经描述过， <BR>即如果发送忙，置tbusy为1。处理完发送数据后，在发送结束中断里清tbusy，同 <BR>时用mark_bh()调用通知系统继续发送。 <BR>但在具体实现我的驱动程序时发现，这样的处理系统好象并不能及时地知道硬 <BR>件已经空闲了，即在mark_bh()以后，系统要等一段时间才会接着发送。造成发送 <BR>效率很低。2M线路只有10%不到的使用率。内核版本为2.0.35。 <BR>我最后的实现是不把tbusy置1，让系统始终认为硬件空闲，但是报告发送不成 <BR>功。系统会一直尝试重发。这样处理就运行正常了。但是遍循内核源码中的网络驱 <BR>动程序，似乎没有这样处理的。不知道症结在哪里。 <BR><BR>3.3 流量控制(flow control) <BR>网络数据的发送和接收都需要流量控制。这些控制是在系统里实现的，不需要 <BR>驱动程序做工作。每个设备数据结构里都有一个参数dev-&gt;tx_queue_len，这个参数 <BR>标明发送时最多缓存的数据包。在Linux系统里以太网设备(10/100Mbps) <BR>tx_queue_len一般设置为100，串行线路(异步串口)为10。实际上如果看源码可以 <BR>知道，设置了dev-&gt;tx_queue_len并不是为缓存这些数据申请了空间。这个参数只是 <BR>在收到协议层的数据包时判断发送队列里的数据是不是到了tx_queue_len的限度， <BR>以决定这一包数据加不加进发送队列。发送时另一个方面的流控是更高层协议的发 <BR>送窗口(TCP协议里就有发送窗口)。达到了窗口大小，高层协议就不会再发送数据。 <BR>接收流控也分两个层次。Netif_rx()缓存的数据包有限制。另外高层协议也会 <BR>有一个最大的等待处理的数据量。 <BR><BR>发送和接收流控处理在net/core/dev.c的do_dev_queue_xmit()和netif_rx() <BR>中。 <BR><BR>3.4 调试 <BR>很多Linux的驱动程序都是编译进内核的，形成一个大的内核文件。但对调试 <BR>来说，这是相当麻烦的。调试驱动程序可以用module方式加载。支持模块方式的 <BR>驱动程序必须提供两个函数：int init_module(void)和void cleanup_module(void)。 <BR>Init_module()在加载此模块时调用，在这个函数里可以register_netdev()注册 <BR>设备。Init_module()返回0表示成功，返回负表示失败。Cleanup_module()在驱动 <BR>程序被卸载时调用，清除占用的资源，调用unregister_netdev()。 <BR>模块可以动态地加载、卸载。在2.0.xx版本里，还有kerneld自动加载模块， <BR>但是2.2.xx中已经取消了kerneld。手工加载使用insmod命令，卸载用rmmod命令， <BR>看内核中的模块用lsmod命令。 <BR>编译驱动程序用gcc，主要命令行参数-DKERNEL -DMODULE。并且作为模块加载 <BR>的驱动程序，只编译成obj形式(加-c参数)。编译好的目标文件放在/lib/modules <BR>/2.x.xx/misc下，在启动文件里用insmod加载。 <BR><BR><BR>四.进一步的阅读 <BR>Linux程序设计资料可以从网上获得。这就是开放源代码的好处。并且没有什 <BR>么“未公开的秘密”。我编写驱动程序时参阅的主要资料包括： <BR>Linux内核源代码 <BR>&lt;&gt; by Michael K. Johnson <BR>&lt;&gt; by Ori Pomerantz <BR>&lt;&gt; by olly in BBS水木清华站 <BR>可以选择一个模板作为开始，内核源代码里有一个网络驱动程序的模板，drivers/net/ <BR>skeleton.c。里面包含了驱动程序的基本内容。但这个模板是以以太 <BR>网设备为对象的，以太网的处理在Linux系统里有特殊“待遇”，所以如果不是以 <BR>太网设备，有些细节上要注意，主要在初始化程序里。 <BR>最后，多参照别人写的程序，听听其他开发者的经验之谈大概是最有效的帮助 <BR>了。 </DIV><img src ="http://www.cnitblog.com/zouzheng/aggbug/7166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-03-04 09:30 <a href="http://www.cnitblog.com/zouzheng/articles/7166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>