সিটিতে সিরিয়াল বন্দর থেকে কীভাবে খুলতে, পড়তে এবং লিখতে হয়?


139

সিরিয়াল বন্দরে পড়া এবং লেখার বিষয়ে আমি কিছুটা বিভ্রান্ত হয়ে পড়েছি। আমার লিনাক্সে একটি ইউএসবি ডিভাইস রয়েছে যা এফটিডিডিআই ইউএসবি সিরিয়াল ডিভাইস রূপান্তরকারী ড্রাইভার ব্যবহার করে। আমি যখন এটি প্লাগ ইন করি তখন এটি তৈরি করে: / dev / ttyUSB1।

আমি ভেবেছিলাম সি থেকে এটি খুলতে এবং পড়তে / লিখতে সহজ হবে I

আমি কি কিছু মিস করছি, বা কেউ আমাকে সঠিক দিকে নির্দেশ করতে পারে?



1
সম্পাদনা: আমি রিব্রামের লিঙ্কটি দেখতে চাই। যাইহোক, বিন্দুটি থেকে যায় যে সিরিয়াল ডিভাইসগুলি একটি ফাইল হিসাবে উপস্থাপিত হওয়ার সময়, ডিভাইসগুলিতে প্রায়শই সিস্টেম কল ioctlএবং এর মাধ্যমে আরও নির্দিষ্ট নির্দিষ্ট ইন্টারফেস প্রয়োগ করা হয় fcntl
মিঃ শিকাড্যান্স


1
ইউনিক্স টের্মিয়োস ভিএমআইএন এবং ভিটিটাইম বোঝা ভিটিআইএম এবং ভিএমআইএন বোঝার জন্য একটি দুর্দান্ত উত্স যা সিরিয়াল পোর্টে একটি পঠন () এর ব্লকিং বৈশিষ্ট্যগুলি পরিচালনা করতে ব্যবহৃত হয়।
ফ্লাক 37

প্রথম মন্তব্যে উল্লিখিত হিসাবে ফ্রিকারের "সিরিয়াল প্রোগ্রামিং হাওটো" থেকে কোড ব্যবহার করবেন না। এগুলি পসিক্স অনুগত হওয়ার জন্য লেখা হয় না, সুতরাং কোড উদাহরণগুলি পোর্টেবল নয় এবং আপনার জন্য নির্ভরযোগ্যভাবে কাজ নাও করতে পারে।
22-25

উত্তর:


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
@ওয়ালেক: আমার কম্পিউটারে ttyUSB নামে কোনও ফাইল নেই, কেবলমাত্র ইউএসবি নামের ফাইলগুলি "ইউএসবিমন"। তবে পিসিতে অনেকগুলি ইউএসবি পোর্ট রয়েছে। তাহলে আমি কীভাবে তাদের কনফিগার করব?
বাস

3
@ বাস: এটি যদি লিনাক্স lsusbহয় তবে সমস্ত ইউএসবি ডিভাইস দেখতে কমান্ডটি ব্যবহার করুন । যদি আপনার সিস্টেমে কাস্টম udevবিধি থাকে তবে তাদের আলাদা আলাদা নাম দেওয়া যেতে পারে ; দেখুন /etc/udev/rules.d/ সেখান থেকে আপনি যে বন্দরটি সন্ধান করছেন তা বেছে নিতে পারেন। অবশ্যই তালিকাভুক্ত করে এবং তারপরে পোর্টটি আন / প্লাগ করে আপনি পার্থক্যটি সনাক্ত করতে পারবেন।
wallyk

1
@ ওয়ালিক আমি স্পেস প্যারিটি (প্যারেনবি | সিএমএসপিআরআর) ব্যবহার করে কোনও আউটপুট (লিখতে সক্ষম নয়) পেতে সক্ষম নই। তবে আমি মার্ক প্যারিটির সাথে যোগাযোগ করতে সক্ষম। কোনও ধারণা কীভাবে এটি সমাধান করবেন?
বাস

5
এই কোডের একটি সমালোচনার জন্য দেখুন stackoverflow.com/questions/25996171/…
করাতাল

2
যেমনটি আমি একটি ttyUSB0 ডিভাইসে ডেটা প্রেরণ করেছি এবং এটি আমার টিটিআই ডিভাইস থেকে এসেছে যা আমি আসলে ব্যবহার করছি। আমি এই কোডটি ব্যবহার করে আক্ষরিকভাবে নিজের টার্মিনালের স্প্যামিং করছি। কাঠের কাঠের নীচের উত্তরটি একটি নিরাপদ বাস্তবায়ন।
পেঁচা

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);
}

প্রোগ্রামটি প্রাপ্ত উপাত্তকে ASCII কোড হিসাবে বিবেচনা করতে, DISPLAY_STRING প্রতীক সহ প্রোগ্রামটি সংকলন করুন, উদাহরণস্বরূপ

 cc -DDISPLAY_STRING demo.c

যদি প্রাপ্ত ডেটাটি ASCII পাঠ্য হয় (বাইনারি ডেটার পরিবর্তে) এবং আপনি এটি নিউলাইন চরিত্রের দ্বারা সমাপ্ত লাইন হিসাবে পড়তে চান তবে একটি নমুনা প্রোগ্রামের জন্য এই উত্তরটি দেখুন ।


1
যে ঠিক cfmakerawডান সঙ্গে প্রতিস্থাপন করা যেতে পারে ?
সিএমসিডিগ্রাঙ্কাই

অন্যান্য উদাহরণগুলি আমি দেখেছি O_NDELAYবা এর সাথে বন্দরটিও খোল O_NONBLOCKCmrr.umn.edu/~strupp/serial.html উল্লেখ করেছেন যে যদি আপনি ঐ পতাকা ফাইল বর্ণনাকারী খুলুন, তারপর VTIMEউপেক্ষা করা হয়। তারপরে O_NONBLOCKফাইল বর্ণনাকারীর সাথে দৌড়ানোর সাথে পার্থক্য কী VTIME?
সিএমসিডিগ্রাগনকাই

@CMCDragonkai - আপনি যা লিখেছেন তা অনেক জটিল। স্ট্যাকওভারফ্লো.com/ প্রশ্নগুলি / 25996171/… দেখুন যা এই প্রশ্নের গৃহীত উত্তরের উল্লেখ করে। বিটিডব্লিউ এমনকি যদি আপনি ননব্লকিং মোডে টার্মিনালটি খোলেন, আপনি এখনও একটি fcntl ()
কাঠের কাঠের

নবাগত প্রশ্নের জন্য দুঃখিত তবে আপনি মূলত লুপটি করার সময় কোথায় করছেন প্রস্থান করছেন বা এটি চিরতরে লুপ করবেন?
বাকালোলো

1
@ বাকালোলো - চিরকাল প্রাপ্তি এবং প্রদর্শন করার জন্য এটি কেবলমাত্র সহজ ডেমো কোড। উদ্দেশ্যটি হ'ল পোর্টেবল কোড যা সংকলন করবে (ডাব্লু / ও ত্রুটি) এবং নির্ভরযোগ্যভাবে কাজ করবে (অন্য উত্তরগুলির বিপরীতে)। বার্তার শেষ নির্ধারণের জন্য একটি পরীক্ষা যুক্ত করা যেতে পারে; কাঁচা ডেটা সহ কোনও বার্তা প্যাকেটের সংজ্ঞা প্রোটোকলের উপর নির্ভর করে। অথবা এই কোডটি পরিবর্তিত হতে পারে কেবলমাত্র অন্য থ্রেডের প্রক্রিয়া করার জন্য প্রাপ্ত বিজ্ঞপ্তিগুলি একটি বিজ্ঞপ্তি বাফারে সংরক্ষণ করতে পারে, যেমন এই উত্তরে বর্ণিত ।
d
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.