C में सीरियल पोर्ट से कैसे खोलें, पढ़ें और लिखें?


139

मैं सीरियल पोर्ट को पढ़ने और लिखने को लेकर थोड़ा उलझन में हूं। मेरे पास लिनक्स में एक USB डिवाइस है जो FTDI USB सीरियल डिवाइस कनवर्टर ड्राइवर का उपयोग करता है। जब मैं इसे प्लग करता हूं, तो यह बनाता है: / dev / ttyUSB1।

मुझे लगा कि सी। में इसे खोलना और पढ़ना / लिखना सरल है। मैं बॉड रेट और समता की जानकारी जानता हूं, लेकिन ऐसा लगता है कि इसके लिए कोई मानक नहीं है?

क्या मुझे कुछ याद आ रहा है, या कोई मुझे सही दिशा में इंगित कर सकता है?



1
संपादित करें: मैं रिब्रम के लिंक को देखूंगा। हालाँकि, यह बात बनी हुई है कि एक सीरियल डिवाइस को एक फाइल के रूप में दर्शाया गया है, जबकि डिवाइस में सिस्टम कॉल के माध्यम से लागू होने वाले उपकरणों में अक्सर अधिक विशिष्ट इंटरफेस होते हैं ioctlऔर fcntl
श्री शिखाडांस


1
UNIX टर्मिनस को समझना VMIN और VTIME VTIME और VMIN को समझने के लिए एक महान संसाधन है जो एक सीरियल पोर्ट पर रीड () की अवरुद्ध विशेषताओं को संभालने के लिए उपयोग किया जाता है।
flak37

पहली टिप्पणी में उल्लिखित "सीरियस प्रोग्रामिंग HOWTO" से कोड का उपयोग न करें। उन्हें POSIX का अनुपालन करने के लिए नहीं लिखा गया है, इसलिए कोड उदाहरण पोर्टेबल नहीं हैं और आपके लिए मज़बूती से काम नहीं कर सकते हैं।
चूरा

जवाबों:


247

मैंने इसे बहुत पहले लिखा था ( वर्ष 1985-1992 से, तब से कुछ ही ट्वीक के साथ ), और प्रत्येक प्रोजेक्ट में आवश्यक बिट्स को कॉपी और पेस्ट करें।

आप कॉल करना होगा cfmakerawएक पर ttyसे प्राप्त tcgetattr। आप नहीं शून्य बाहर एक कर सकते हैं struct termios, यह कॉन्फ़िगर, और फिर सेट ttyके साथ tcsetattr। यदि आप शून्य-आउट विधि का उपयोग करते हैं, तो आप विशेष रूप से बीएसडी और ओएस एक्स पर अस्पष्टीकृत रुक-रुक कर असफलता का अनुभव करेंगे। "अनएक्सप्लेनड इंटरमिटेंट फेलियर" में फांसी शामिल है read(3)

#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <unistd.h>

int
set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void
set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


...
char *portname = "/dev/ttyUSB1"
 ...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
        error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
        return;
}

set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0);                // set no blocking

write (fd, "hello!\n", 7);           // send 7 character greeting

usleep ((7 + 25) * 100);             // sleep enough to transmit the 7 plus
                                     // receive 25:  approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf);  // read up to 100 characters if ready to read

गति के लिए मूल्य हैं B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, आदि समता के लिए मान रहे हैं 0(कोई समता अर्थ), PARENB|PARODD(समता सक्षम और अजीब का उपयोग करें), PARENB(समता सक्षम और यहां तक कि का उपयोग करें), PARENB|PARODD|CMSPAR(निशान समता), औरPARENB|CMSPAR ( अंतरिक्ष समता)।

"ब्लॉकिंग" सेट करता है कि read()पोर्ट पर वर्णों की निर्दिष्ट संख्या के आने की प्रतीक्षा है या नहीं। कोई भी अवरोध स्थापित करने का अर्थ यह नहीं है कि read()बफर वर्ण सीमा तक अधिक रिटर्न की प्रतीक्षा किए बिना कई रिटर्न उपलब्ध हैं।


परिशिष्ट:

CMSPARकेवल चिह्न और अंतरिक्ष समता चुनने के लिए आवश्यक है, जो असामान्य है। अधिकांश अनुप्रयोगों के लिए, इसे छोड़ा जा सकता है। मेरी हेडर फ़ाइल केवल तभी /usr/include/bits/termios.hपरिभाषा को सक्षम करती है CMSPARजब प्रीप्रोसेसर प्रतीक __USE_MISCपरिभाषित किया गया हो। यह परिभाषा (में features.h) के साथ होती है

#if defined _BSD_SOURCE || defined _SVID_SOURCE
 #define __USE_MISC     1
#endif

<features.h>कहते हैं की परिचयात्मक टिप्पणियाँ :

/* These are defined by the user (or the compiler)
   to specify the desired environment:

...
   _BSD_SOURCE          ISO C, POSIX, and 4.3BSD things.
   _SVID_SOURCE         ISO C, POSIX, and SVID things.
...
 */

1
@wallyk: मेरे कंप्यूटर में ttyUSB नाम की कोई फाइल नहीं है, केवल USB नाम की फाइलें "usbmon" हैं। लेकिन पीसी में बहुत सारे यूएसबी पोर्ट होते हैं। तो मैं उन्हें कैसे कॉन्फ़िगर करूं?
बास

3
@ बैस: यदि यह लिनक्स है, तो lsusbसभी यूएसबी उपकरणों को देखने के लिए कमांड का उपयोग करें । यदि आपके सिस्टम में कस्टम udevनियम हैं, तो उन्हें अलग नाम दिया जा सकता है; देखें /etc/udev/rules.d/ कि शायद वहाँ से आप उस बंदरगाह को चुन सकते हैं जिसे आप ढूंढ रहे हैं। निश्चित रूप से लिस्टिंग और फिर पोर्ट को अन / प्लग करने से आप अंतर को पहचान सकते हैं।
वैलीक

1
@ wallyk मैं स्पेस समानता (PARENB | CMSPRAR) का उपयोग करके कोई भी आउटपुट (लिखने में सक्षम नहीं) प्राप्त करने में सक्षम नहीं हूं। लेकिन मैं मार्क समानता के साथ संवाद करने में सक्षम हूं। किसी भी विचार यह कैसे हल करने के लिए?
बास

5
इस कोड की समालोचना के लिए देखें stackoverflow.com/questions/25996171/…
चूरा

2
जैसा कि मैंने डेटा को एक ttyUSB0 डिवाइस में भेजा और यह मेरे tty डिवाइस से बाहर आया जिसका मैं वास्तव में उपयोग कर रहा था। मैं सचमुच इस कोड का उपयोग करके अपने स्वयं के टर्मिनल को स्पैम कर रहा था। चूरा से नीचे का उत्तर एक सुरक्षित कार्यान्वयन है।
उल्लू

50

डेमो कोड के लिए जो पोसिक्स मानक के अनुरूप है, जैसा कि टर्मिनल मोड्स को उचित रूप से सेट करने में वर्णित है पोसिक्स ऑपरेटिंग सिस्टम के लिए और सीरियल प्रोग्रामिंग गाइड की , निम्नलिखित की पेशकश की जाती है।
यह अनिवार्य रूप से अन्य उत्तर से लिया गया है, लेकिन गलत और भ्रामक टिप्पणियों को सही किया गया है।

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    char *portname = "/dev/ttyUSB0";
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /* simple output */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple noncanonical input */
    do {
        unsigned char buf[80];
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
#ifdef DISPLAY_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
            unsigned char   *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Timeout from read\n");
        }               
        /* repeat read to get full message */
    } while (1);
}

प्रोग्राम को प्राप्त डेटा को एएससीआईआई कोड के रूप में मानने के लिए, प्रोग्राम को प्रतीक DISPLAY_STRING, उदाहरण के साथ संकलित करें

 cc -DDISPLAY_STRING demo.c

यदि प्राप्त डेटा ASCII टेक्स्ट (बाइनरी डेटा के बजाय) है और आप इसे न्यूलाइन वर्ण द्वारा समाप्त लाइनों के रूप में पढ़ना चाहते हैं, तो एक नमूना कार्यक्रम के लिए इस उत्तर को देखें ।


1
उस की जगह बस cfmakerawसही के साथ बदला जा सकता है ?
CMCDragonkai

अन्य उदाहरण जो मैंने देखे हैं, वे पोर्ट को O_NDELAYया उसके साथ भी खोलते हैं O_NONBLOCKCmrr.umn.edu/~strupp/serial.html कहा गया है कि अगर आप उन झंडे के साथ फ़ाइल वर्णनकर्ता खोलें, फिर VTIMEनजरअंदाज कर दिया है। फिर O_NONBLOCKफ़ाइल डिस्क्रिप्टर बनाम इसे करने के साथ चलने में क्या अंतर है VTIME?
CMCDragonkai

@CMCDragonkai - आपने जो लिखा है, उससे कहीं अधिक जटिल है। Stackoverflow.com/questions/25996171/… देखें जो इस प्रश्न के स्वीकृत उत्तर को संदर्भित करता है। BTW भले ही आप नॉनब्लॉकिंग मोड में टर्मिनल खोलते हों, फिर भी आप fcntl ()
चूरा

नौसिखिया सवाल के लिए क्षमा करें, लेकिन आप मुख्य में लूप करते समय क्या कर रहे हैं या यह हमेशा के लिए लूप करता है?
बकलोलो

1
@bakalolo - यह हमेशा के लिए प्राप्त करने और प्रदर्शित करने के लिए केवल सरल डेमो कोड है। आशय पोर्टेबल कोड है जो (w / o त्रुटियों) को संकलित करेगा और मज़बूती से काम करेगा (अन्य उत्तर के विपरीत)। संदेश के अंत को निर्धारित करने के लिए एक परीक्षण जोड़ा जा सकता है; कच्चे डेटा के साथ एक संदेश पैकेट की परिभाषा प्रोटोकॉल पर निर्भर करती है। या इस कोड को संशोधित किया जा सकता है ताकि प्राप्त डेटा को किसी अन्य थ्रेड के लिए एक परिपत्र बफ़र में स्टोर किया जा सके, जैसे कि इस उत्तर में वर्णित है ।
चूरा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.