日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 串口编程四 串口设备程序开发

發(fā)布時(shí)間:2023/12/9 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 串口编程四 串口设备程序开发 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Linux 串口編程和程序相對來說是很簡單的,之所以用博客連載來展示,主要是想在學(xué)會(huì)使用的基礎(chǔ)上掌握相關(guān)背景,原理以及注意事項(xiàng)。相信在遇到問題的時(shí)候,我們就不會(huì)對于技術(shù)的概念和 API 的使用淺嘗輒止了。下面進(jìn)入具體應(yīng)用案例,由于現(xiàn)在很多電腦已經(jīng)沒有引出串口以及波特率范圍會(huì)受到限制,這里我以 CH340 USB 轉(zhuǎn)串口芯片制作的模塊為基礎(chǔ)講解串口應(yīng)用程序開發(fā),關(guān)于該芯片在 Linux 系統(tǒng)的使用以及驅(qū)動(dòng)加載可以參考:CH340 Linux驅(qū)動(dòng)使用教程。

設(shè)備的打開與關(guān)閉

1. int libtty_open(const char *devname);

函數(shù)功能:根據(jù)傳入的串口設(shè)備名打開相應(yīng)的設(shè)備。成功返回設(shè)備句柄,失敗返回-1。

2. int libtty_close(int fd);

函數(shù)功能:關(guān)閉打開的設(shè)備句柄。成功返回0,失敗返回負(fù)值。

設(shè)備的配置

1. int libtty_setopt(int fd, int speed, char databits, char stopbits, char parity);

函數(shù)功能:配置串口設(shè)備,傳入?yún)?shù)依次為波特率設(shè)置、數(shù)據(jù)位設(shè)置、停止位設(shè)置、檢驗(yàn)設(shè)置。

Notes:設(shè)備打開前,可以通過 ls /dev 確認(rèn)自己的硬件設(shè)備名,對于 USB 轉(zhuǎn)串口 IC,在系統(tǒng)下名稱為 "ttyUSBx",設(shè)備序號(hào)是根據(jù)插入主機(jī)的先后順序自動(dòng)分配的,這里我的為 "ttyUSB0",讀者根據(jù)自己的需要修改。

/*** libtty_open - open tty device* @devname: the device name to open** In this demo device is opened blocked, you could modify it at will.*/ int libtty_open(const char *devname) {int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); int flags = 0;if (fd == -1) { perror("open device failed");return -1; }flags = fcntl(fd, F_GETFL, 0);flags &= ~O_NONBLOCK;if (fcntl(fd, F_SETFL, flags) < 0) {printf("fcntl failed.\n");return -1;}if (isatty(fd) == 0){printf("not tty device.\n");return -1;}elseprintf("tty device test ok.\n");return fd; }

Note:
  • 傳入的 devname 參數(shù)為設(shè)備絕對路徑;
  • O_NOCTTY標(biāo)志用于通知系統(tǒng),這個(gè)程序不會(huì)成為對應(yīng)這個(gè)設(shè)備的控制終端。如果沒有指定這個(gè)標(biāo)志,那么任何一個(gè)輸入(如SIGINT等)都將會(huì)影響用戶的進(jìn)程;
  • O_NDELAY標(biāo)志與O_NONBLOCK 等效,但這里不僅僅是設(shè)置為非阻塞,還用于通知系統(tǒng),這個(gè)程序不關(guān)心 DCD 信號(hào)線所處的狀態(tài)(即與設(shè)備相連的另一端是否激活或者停止)。如果用戶指定了這一標(biāo)志,則進(jìn)程將會(huì)一直處在休眠狀態(tài),直到 DCD 信號(hào)線被激活;
  • 使用 fcntl 函數(shù)恢復(fù)設(shè)備狀態(tài)為阻塞狀態(tài),在數(shù)據(jù)收發(fā)時(shí)就會(huì)進(jìn)行等待;
  • 使用 isatty函數(shù)測試當(dāng)前打開的設(shè)備句柄是否關(guān)聯(lián)到終端設(shè)備,如果不是 tty 設(shè)備,那么也返回出錯(cuò);
/*** libtty_setopt - config tty device* @fd: device handle* @speed: baud rate to set* @databits: data bits to set* @stopbits: stop bits to set* @parity: parity set** The function return 0 if success, or -1 if fail.*/ int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity) {struct termios newtio;struct termios oldtio;int i;bzero(&newtio, sizeof(newtio));bzero(&oldtio, sizeof(oldtio));if (tcgetattr(fd, &oldtio) != 0) {perror("tcgetattr"); return -1; }newtio.c_cflag |= CLOCAL | CREAD;newtio.c_cflag &= ~CSIZE;/* set tty speed */for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) { cfsetispeed(&newtio, speed_arr[i]); cfsetospeed(&newtio, speed_arr[i]); } }/* set data bits */switch (databits) {case 5: newtio.c_cflag |= CS5;break;case 6: newtio.c_cflag |= CS6;break;case 7: newtio.c_cflag |= CS7;break;case 8: newtio.c_cflag |= CS8;break; default: fprintf(stderr, "unsupported data size\n");return -1; }/* set parity */switch (parity) { case 'n':case 'N':newtio.c_cflag &= ~PARENB; /* Clear parity enable */newtio.c_iflag &= ~INPCK; /* Disable input parity check */break; case 'o': case 'O': newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */newtio.c_iflag |= INPCK; /* Enable input parity check */break; case 'e': case 'E': newtio.c_cflag |= PARENB; /* Enable parity */ newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */ newtio.c_iflag |= INPCK; /* Enable input parity check */break;default: fprintf(stderr, "unsupported parity\n");return -1; } /* set stop bits */ switch (stopbits) { case 1: newtio.c_cflag &= ~CSTOPB; break;case 2: newtio.c_cflag |= CSTOPB; break;default: perror("unsupported stop bits\n"); return -1;}newtio.c_cc[VTIME] = 0; /* Time-out value (tenths of a second) [!ICANON]. */newtio.c_cc[VMIN] = 0; /* Minimum number of bytes read at once [!ICANON]. */tcflush(fd, TCIOFLUSH); if (tcsetattr(fd, TCSANOW, &newtio) != 0) {perror("tcsetattr");return -1;}return 0; }Note:
  • 首先保存了原先串口配置,為了方便演示,這里保存為局部變量,實(shí)際使用時(shí)是需要把配置保存到全局 termios 結(jié)構(gòu)體中的。使用tcgetattr還可以測試配置是否正確、串口是否可用等。返回值參見 man 手冊;
  • 使用 CLOCAL 用于忽略所有 MODEM 狀態(tài)信號(hào)線,CREAD 標(biāo)志用于使能接收。CSIZE 為數(shù)據(jù)位掩碼;
  • 調(diào)用 cfsetispeedcfsetospeed參數(shù)設(shè)置波特率,函數(shù)中引用了兩個(gè)數(shù)組,在后面的完整代碼中會(huì)看到,這樣書寫可以簡化設(shè)置代碼;
  • 通過控制 c_cflag 與 c_iflag 配置串口數(shù)據(jù)位、停止位以及校驗(yàn)設(shè)置等;
  • VTIMEVMIN作用已經(jīng)講解多次,不再贅述,值得注意的是,TIME 值的單位是十分之一秒
  • 通過 tcflush清空輸入和輸出緩沖區(qū),根據(jù)實(shí)際需要修改;
  • 最后通過 tcsetattr 函數(shù)對將配置實(shí)際作用于串口;
數(shù)據(jù)讀寫直接使用 readwrite 函數(shù)接口就可以了,因此沒有列舉出來。下面給出完整的串口讀寫測試代碼: /* TTY testing utility (using tty driver)* Copyright (c) 2017* This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License.** Cross-compile with cross-gcc -I /path/to/cross-kernel/include*/#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> int speed_arr[] = {B115200,B57600,B38400,B19200,B9600,B4800,B2400,B1200,B300 };int name_arr[] = {115200,57600,38400,19200,9600,4800,2400,1200,300 };/*** libtty_setopt - config tty device* @fd: device handle* @speed: baud rate to set* @databits: data bits to set* @stopbits: stop bits to set* @parity: parity set** The function return 0 if success, or -1 if fail.*/ int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity) {struct termios newtio;struct termios oldtio;int i;bzero(&newtio, sizeof(newtio));bzero(&oldtio, sizeof(oldtio));if (tcgetattr(fd, &oldtio) != 0) {perror("tcgetattr"); return -1; }newtio.c_cflag |= CLOCAL | CREAD;newtio.c_cflag &= ~CSIZE;/* set tty speed */for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) { cfsetispeed(&newtio, speed_arr[i]); cfsetospeed(&newtio, speed_arr[i]); } }/* set data bits */switch (databits) {case 5: newtio.c_cflag |= CS5;break;case 6: newtio.c_cflag |= CS6;break;case 7: newtio.c_cflag |= CS7;break;case 8: newtio.c_cflag |= CS8;break; default: fprintf(stderr, "unsupported data size\n");return -1; }/* set parity */switch (parity) { case 'n':case 'N':newtio.c_cflag &= ~PARENB; /* Clear parity enable */newtio.c_iflag &= ~INPCK; /* Disable input parity check */break; case 'o': case 'O': newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */newtio.c_iflag |= INPCK; /* Enable input parity check */break; case 'e': case 'E': newtio.c_cflag |= PARENB; /* Enable parity */ newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */ newtio.c_iflag |= INPCK; /* Enable input parity check */break;default: fprintf(stderr, "unsupported parity\n");return -1; } /* set stop bits */ switch (stopbits) { case 1: newtio.c_cflag &= ~CSTOPB; break;case 2: newtio.c_cflag |= CSTOPB; break;default: perror("unsupported stop bits\n"); return -1;}newtio.c_cc[VTIME] = 0; /* Time-out value (tenths of a second) [!ICANON]. */newtio.c_cc[VMIN] = 0; /* Minimum number of bytes read at once [!ICANON]. */tcflush(fd, TCIOFLUSH); if (tcsetattr(fd, TCSANOW, &newtio) != 0) {perror("tcsetattr");return -1;}return 0; }/*** libtty_open - open tty device* @devname: the device name to open** In this demo device is opened blocked, you could modify it at will.*/ int libtty_open(const char *devname) {int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); int flags = 0;if (fd == -1) { perror("open device failed");return -1; }flags = fcntl(fd, F_GETFL, 0);flags &= ~O_NONBLOCK;if (fcntl(fd, F_SETFL, flags) < 0) {printf("fcntl failed.\n");return -1;}if (isatty(fd) == 0){printf("not tty device.\n");return -1;}elseprintf("tty device test ok.\n");return fd; }/*** libtty_close - close tty device* @fd: the device handle**/ int libtty_close(int fd) {return close(fd); }void tty_test(int fd) {int nwrite, nread;char buf[100];memset(buf, 0x32, sizeof(buf));while (1) {nwrite = write(fd, buf, sizeof(buf));printf("wrote %d bytes already.\n", nwrite);nread = read(fd, buf, sizeof(buf));printf("read %d bytes already.\n", nread);sleep(2);}}int main(int argc, char *argv[]) {int fd;int ret;fd = libtty_open("/dev/ttyUSB0");if (fd < 0) {printf("libtty_open error.\n");exit(0);}ret = libtty_setopt(fd, 9600, 8, 1, 'n');if (ret != 0) {printf("libtty_setopt error.\n");exit(0);}tty_test(fd);ret = libtty_close(fd);if (ret != 0) {printf("libtty_close error.\n");exit(0);} } 執(zhí)行成功的話,會(huì)在終端屏幕上看到每隔兩秒輸出串口成功發(fā)送和接收的字節(jié)數(shù),測試時(shí)可以直接短接串口的發(fā)送和接收引腳進(jìn)行測試。以下為成功測試截圖:

關(guān)于?Linux?串口編程的其他文章,可以移步至以下鏈接:

  • 《Linux 串口編程<一> 一些背景》
  • 《Linux 串口編程<二> 深入了解 termios》
  • 《Linux 串口編程<三> 使用termios與API 進(jìn)行串口程序開發(fā)》
  • 《Linux 串口編程<四> 串口設(shè)備程序開發(fā)》

  • 有疑問的讀者可以給我郵件或者評(píng)論,覺得對你有幫助就點(diǎn)贊吧~:-D


    總結(jié)

    以上是生活随笔為你收集整理的Linux 串口编程四 串口设备程序开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。