前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux uart应用开发(ttyS*设备)《Rice linux 学习开发》

linux uart应用开发(ttyS*设备)《Rice linux 学习开发》

作者头像
Rice加饭
发布2022-05-09 21:01:50
3.2K0
发布2022-05-09 21:01:50
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式

写这篇文章的原因:因为在linux开发串口应用的时候,遇到了问题,让遇到相同问题的人少走点弯路:

①读串口数据的时,需要接受换行符才能返回。

②接受数据时,一个字节一个字节的返回。无法接受完多个字节在返回。

对于 linux的开发板来说,串口的驱动是不需要我们去开发,我目前是在内核4.9上开发,只需要修改一下设备树就可以了。所以直接对设备文件进访问就可以了。

linux使用串口的方法:

1.串口配置的头文件:

代码语言:javascript
复制
#include  <termios.h>    /*POSIX 终端控制定义*/

2.串口波特率的设置:

波特率

B4800

4800

B9600

9600

B67500

67500

B115200

115200

修改波特率是使用cfsetispeed()和cfsetospeed()函数来操作。

例如:要将波特率修改为115200:

cfsetispeed(&opts, B115200);

cfsetospeed(&opts,B115200);

3.串口属性配置:

串口的属性定义在结构体struct termios,其头文件#include<asm/termbits.h>,结构体定义如下:

代码语言:javascript
复制
#define NCCS 19

struct termios {
    tcflag_t c_iflag;      /* 输入参数 */
    tcflag_t c_oflag;      /* 输出参数 */
    tcflag_t c_cflag;      /* 控制参数*/
    tcflag_t c_lflag;      /* 本地模式 */
    tcflag_t c_ispeed;     /* 输入波特率 */
    tcflag_t c_ospeed;     /* 输出波特率 */
    cc_t c_line;           /* 线控制 */
    cc_t c_cc[NCCS];       /* 控制字符*/
}

对串口属性的获取和设置有两个:

① tcsetattr(int fd, int opt_DE, termios *ptr) ,返回值:成功返回0,失败返回-1。

fd:文件描述符

opt_DE:选项值,可供选择:

(1)TCSANOW: 不等数据传输完毕就立即改变属性

(2)TCSADRAIN:等待所有数据传输结束才改变属性

(3)TCSAFLUSH:清空输入输出缓冲区才改变属性

*ptr:指向termios结构的指针

②tcgetsttr(int fd, termios *ptr),返回值:成功返回0,失败返回-1。

fd:待操作的文件描述符

*ptr:指向termios结构的指针

4.属性描述:

①:c_iflag:

IGNBRK:忽略输入中的 BREAK 状态。

BRKINT:如果设置了 IGNBRK,将忽略 BREAK。如果没有设置,但是设置了 BRKINT,那么 BREAK 将使得输入和输出队列被刷新,如果终端是一个前台进程组的控制终端,这个进程组中所有进程将收到 SIGINT 信号。如果既未设置IGNBRK 也未设置 BRKINT,BREAK 将视为与 NUL 字符同义,除非设置了 PARMRK,这种情况下它被视为序列 /377 /0 /0。

IGNPAR:忽略桢错误和奇偶校验错。

PARMRK:如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字符前插入 /377 /0。如果既没有设置 IGNPAR 也没有设置PARMRK,将有奇偶校验错或桢错误的字符视为 /0。

INPCK:启用输入奇偶检测。

ISTRIP:去掉第八位。

INLCR:将输入中的 NL 翻译为CR。

IGNCR:忽略输入中的回车。

ICRNL:将输入中的回车翻译为新行 (除非设置了 IGNCR)。

IUCLC:(不属于 POSIX) 将输入中的大写字母映射为小写字母。

IXON:启用输出的 XON/XOFF 流控制。

IXANY:(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出。(?)

IXOFF:启用输入的 XON/XOFF 流控制。

IMAXBEL:(不属于 POSIX) 当输入队列满时响零。Linux 没有实现这一位,总是将它视为已设置。

②c_oflag:

OPOST:启用具体实现自行定义的输出处理。其余 c_oflag 标志常量定义在 POSIX 1003.1-2001 中,除非另外说明。

OLCUC:(不属于 POSIX) 将输出中的小写字母映射为大写字母。

ONLCR:(XSI) 将输出中的新行符映射为回车-换行。

OCRNL:将输出中的回车映射为新行符

ONOCR:不在第 0 列输出回车。

ONLRET:不输出回车。

OFILL:发送填充字符作为延时,而不是使用定时来延时。

OFDEL:(不属于 POSIX) 填充字符是 ASCII DEL (0177)。如果不设置,填充字符则是 ASCII NUL。

NLDLY:新行延时掩码。取值为 NL0NL1

CRDLY:回车延时掩码。取值为 CR0, CR1, CR2,或 CR3

TABDLY:水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值为 TAB3,即 XTABS,将扩展跳格为空格 (每个跳格符填充 8 个空格)。(?)

BSDLY:回退延时掩码。取值为 BS0BS1。(从来没有被实现过)

VTDLY:竖直跳格延时掩码。取值为 VT0VT1

FFDLY:进表延时掩码。取值为 FF0FF1

③c_cflag:

CBAUD:(不属于 POSIX) 波特率掩码 (4+1 位)。

CBAUDEX:(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在 CBAUD 中。

(POSIX 规定波特率存储在 termios 结构中,并未精确指定它的位置,而是提供了函数 cfgetispeed()cfsetispeed() 来存取它。一些系统使用 c_cflag 中 CBAUD 选择的位,其他系统使用单独的变量,例如 sg_ispeedsg_ospeed 。)

CSIZE:字符长度掩码。取值为 CS5, CS6, CS7, 或 CS8。

CSTOPB:设置两个停止位,而不是一个。

CREAD:打开接受者。

PARENB:允许输出产生奇偶信息以及输入的奇偶校验。

PARODD:输入和输出是奇校验。

HUPCL:在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。(?)

CLOCAL:忽略 modem 控制线。

LOBLK:(不属于 POSIX) 从非当前 shell 层阻塞输出(用于 shl )。(?)

CIBAUD:(不属于 POSIX) 输入速度的掩码。CIBAUD 各位的值与 CBAUD 各位相同,左移了 IBSHIFT 位。

CRTSCTS:(不属于 POSIX) 启用RTS/CTS (硬件) 流控制。

④c_lflag:

ISIG:当接受到字符 INTR,QUIT, SUSP, 或 DSUSP 时,产生相应的信号。

ICANON:启用标准模式 (canonical mode)。允许使用特殊字符EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和WERASE,以及按行的缓冲。

XCASE:(不属于 POSIX; Linux 下不被支持) 如果同时设置了 ICANON,终端只有大写。输入被转换为小写,除了以 / 前缀的字符。输出时,大写字符被前缀 /,小写字符被转换成大写。

ECHO:回显输入字符。

ECHOE:如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词。

ECHOK:如果同时设置了 ICANON,字符 KILL 删除当前行。

ECHONL:如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO。

ECHOCTL:(不属于 POSIX) 如果同时设置了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大0x40 的 ASCII 码。例如,字符 0x08 (BS) 被回显为 ^H。

ECHOPRT:(不属于 POSIX) 如果同时设置了 ICANON 和 IECHO,字符在删除的同时被打印。

ECHOKE:(不属于 POSIX) 如果同时设置了 ICANON,回显 KILL 时将删除一行中的每个字符,如同指定了 ECHOE 和ECHOPRT 一样。

DEFECHO:(不属于 POSIX) 只在一个进程读的时候回显。

FLUSHO:(不属于 POSIX; Linux 下不被支持) 输出被刷新。这个标志可以通过键入字符 DISCARD 来开关。

NOFLSH:禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列。

TOSTOP:向试图写控制终端的后台进程组发送 SIGTTOU 信号。

PENDIN:(不属于 POSIX; Linux 下不被支持) 在读入下一个字符时,输入队列中所有字符被重新输出。(bash 用它来处理 typeahead)

IEXTEN:启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效。

⑤c_cc[],数组定义了特殊的控制字符。符号下标 (初始值) 和意义为:

VINTR:(003, ETX, Ctrl-C, or also0177, DEL, rubout) 中断字符。发出 SIGINT 信号。当设置 ISIG 时可被识别,不再作为输入传递。

VQUIT:(034, FS, Ctrl-/) 退出字符。发出 SIGQUIT 信号。当设置 ISIG 时可被识别,不再作为输入传递。

VERASE:(0177, DEL, rubout, or 010,BS, Ctrl-H, or also #) 删除字符。删除上一个还没有删掉的字符,但不删除上一个 EOF 或行首。当设置 ICANON 时可被识别,不再作为输入传递。

VKILL:(025, NAK, Ctrl-U, or Ctrl-X,or also @) 终止字符。删除自上一个 EOF 或行首以来的输入。当设置 ICANON 时可被识别,不再作为输入传递。

VEOF:(004, EOT, Ctrl-D) 文件尾字符。更精确地说,这个字符使得 tty 缓冲中的内容被送到等待输入的用户程序中,而不必等到 EOL。如果它是一行的第一个字符,那么用户程序的 read() 将返回 0,指示读到了 EOF。当设置ICANON 时可被识别,不再作为输入传递。

VMIN:非 canonical 模式读的最小字符数。

VEOL:(0, NUL) 附加的行尾字符。当设置 ICANON 时可被识别。

VTIME:非 canonical 模式读时的延时,以十分之一秒为单位。

VEOL2:(not in POSIX; 0, NUL) 另一个行尾字符。当设置 ICANON 时可被识别。

VSWTCH:(not in POSIX; not supportedunder Linux; 0, NUL) 开关字符。(只为 shl 所用。)

VSTART:(021, DC1, Ctrl-Q) 开始字符。重新开始被 Stop 字符中止的输出。当设置 IXON 时可被识别,不再作为输入传递。

VSTOP:(023, DC3, Ctrl-S) 停止字符。停止输出,直到键入 Start 字符。当设置 IXON 时可被识别,不再作为输入传递。

VSUSP:(032, SUB, Ctrl-Z) 挂起字符。发送 SIGTSTP 信号。当设置 ISIG 时可被识别,不再作为输入传递。

VDSUSP:(not in POSIX; not supportedunder Linux; 031, EM, Ctrl-Y) 延时挂起信号。当用户程序读到这个字符时,发送 SIGTSTP 信号。当设置 IEXTEN 和 ISIG,并且系统支持作业管理时可被识别,不再作为输入传递。

VLNEXT:(not in POSIX; 026, SYN,Ctrl-V) 字面上的下一个。引用下一个输入字符,取消它的任何特殊含义。当设置IEXTEN 时可被识别,不再作为输入传递。

VWERASE:(not in POSIX; 027, ETB,Ctrl-W) 删除词。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。

VREPRINT:(not in POSIX; 022, DC2,Ctrl-R) 重新输出未读的字符。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。

VDISCARD:(not in POSIX; not supportedunder Linux; 017, SI, Ctrl-O) 开关:开始/结束丢弃未完成的输出。当设置 IEXTEN 时可被识别,不再作为输入传递。

VSTATUS:(not in POSIX; not supportedunder Linux; status request: 024, DC4, Ctrl-T).

⑥:VIME和VMIN需要配合使用,关系如下:

1、VTIME=0,VMIN=0:此时即使读取不到任何数据,函数read也会返回,返回值是0。

2、VTIME=0,VMIN>0:read调用一直阻塞,直到读到VMIN个字符后立即返回。

3、VTIME>0,VMIN=0:read调用读到数据则立即返回,否则将为每个字符最多等待 VTIME*100ms 时间。

4、VTIME>0,VMIN>0:read调用将保持阻塞直到读取到第一个字符,读到了第一个字符之后开始计时,此后若时间到了VTIME*100ms 或者时间未到但已读够了VMIN个字符则会返回。若在时间未到之前又读到了一个字符(但此时读到的总数仍不够VMIN)则计时重新开始(即每个字符都有VTIME*100ms的超时时间)。

代码例程:

①设置波特率:

代码语言:javascript
复制
void set_baudrate(struct termios *set_serial, unsigned long int baud_rate)
{
    int baud = B115200;
    switch(baud_rate)
    {
        case 2400:
            baud = B2400;
            break;
        case 4800:
            baud = B4800;
            break;
        case 9600:
            baud = B9600;
            break;
        case 19200:
            baud = B19200;
            break;
        case 38400:
            baud = B38400;
            break;
        case 57600:
            baud = B57600;
            break;
        case 115200:
            baud = B115200;
            break;
        default:
            baud = B115200;
            break;
    }
    cfsetispeed(set_serial, baud);
    cfsetospeed(set_serial, baud);
}

②设置数据位:

代码语言:javascript
复制
void set_databits(struct termios *set_serial, unsigned int data_bits)
{
    switch(data_bits)
    {
        case 5:
            set_serial->c_cflag |= CS5;
            break;
        case 6:
            set_serial->c_cflag |= CS6;
            break;
        case 7:
            set_serial->c_cflag |= CS7;
            break;
        case 8:
            set_serial->c_cflag |= CS8;
            break;
        default:
            set_serial->c_cflag |= CS8;
            break;
    }
}

③设置校验位:

代码语言:javascript
复制
void set_parity(struct termios *set_serial, char parity)
{
    switch(parity)
    {
        case 'N':
            set_serial->c_cflag &= ~PARENB;     //no parity check
            break;
        case 'O':
            set_serial->c_cflag |= PARENB;      //odd check
            set_serial->c_cflag &= ~PARODD;
            break;
        case 'E':
            set_serial->c_cflag |= PARENB;      //even check
            set_serial->c_cflag |= PARODD;
            break;
        default:
            set_serial->c_cflag &= ~PARENB;
            break;
    }
}

④停止位:

代码语言:javascript
复制
void set_stopbits(struct termios *set_serial, unsigned int stop_bits)
{
    if(stop_bits == 2)
    {
        set_serial->c_cflag |= CSTOPB;  //2 stop bits
    }
    else
    {
        set_serial->c_cflag &= ~CSTOPB; //1 stop bits
    }
}

⑤串口配置函数:

代码语言:javascript
复制
void set_option(unsigned int baud_rate, unsigned int data_bits, char parity, unsigned int stop_bits)
{
    struct termios opts;
    tcgetattr(m_dev, &opts);

    set_baudrate(&opts, baud_rate);

    opts.c_cflag |= CLOCAL|CREAD;

    set_parity(&opts, parity);
    set_stopbits(&opts, stop_bits);
    set_databits(&opts, data_bits);

    opts.c_cflag &= ~CRTSCTS;

    opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw input
    opts.c_oflag &= ~OPOST; // raw output

    opts.c_cc[VTIME]=1;
    opts.c_cc[VMIN]=1023;

    tcsetattr(m_dev, TCSANOW, &opts);
}

⑥发送数据:

代码语言:javascript
复制
int send_data(int fd, const char *data, int datalen)
{
    int len = 0;
    len = write(m_dev, data, datalen);
    if(len == datalen)
    {
        return 0;
    }
    else
    {
        tcflush(m_dev, TCOFLUSH);
        return -1;
    }
}

7 接收函数:

代码语言:javascript
复制
int receive(int fd, char *data, int datalen)
{
    int read_len;
    if((read_len = read(m_dev, data, datalen))>0)
    {
        return read_len;
    }
    else
    {
        return -1;
    }
}

⑧主函数:

代码语言:javascript
复制
int main( int argc, char *argv[])
{
    int fd;
    int ret;
    char buff[1024];
    char senddata[] = "uart";

    fd= open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd <= 0)
    {
        printf("uart open fail\n");
        return -1;
    }
    fcntl(fd, F_SETFL, 0);

    set_option(115200, 8, 'N', 1);


    while (1)
    {
       ret = receive(fd, buff, sizeof(buff));
       buff[ret] = 0;
       printf("receive data : %s\n", buff);

       send_data(fd, senddata, sizeof(senddata));
    }
    close(fd);
    return 0;
}

完整代码:https://download.csdn.net/download/jiafanluo/11231218

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档