Linux下讀取RFID卡號(C串口編程)


由於項目需要用到RFID、GPRS、攝像頭等模塊所以便看了一下,整理了一下學習思路,本篇先是整理一下串口讀取RFID卡號的程序思路,后面還會更其他的得意奮斗

RFID模塊:

    本次采用的是125K的RFID讀卡器和標簽,很容易理解的,其實就是一張卡片里面存了一串數字(這個問題有點像你問一個藝術家洛必達法則是啥再見咦洛必達是啥),然后有個讀卡器,當你把卡片放到讀卡器上時,讀卡器會將卡里面存的卡號讀取出來,然后放到串口發送緩沖區,等待我們去讀取,那么問題就是怎么讀取。

串口讀寫:

    大家都知道。linux下面一切皆文件,設備也不例外,上面提到的串口就是個設備文件,linux設備文件一般存放在“/dev/”下,當你ls的時候會發現一大堆什么ttyS0、sda、video....現在筆記本串口設備文件一般都是ttyUSBx(x=0,1,2...)。既然是文件,那就能打開嘍,不過它不是被“右鍵->打開”,而是被“系統調用open()”。當然不只是把它打開就完了,操作串口有一系列的系統調用。說到系統調用,其實就是系統底層給在上層編寫程序的你提供的一些系統級函數。

    一些需要的頭文件:

  1. #include <unistd.h>       /*linux系統調用*/  
  2. #include <stdlib.h>  
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. #include <fcntl.h>        /*文件控制*/  
  6. #include <sys/stat.h>     /*文件狀態*/  
  7. #include <sys/types.h>    /*定義系統類型,像size_t等*/  
  8. #include <errno.h>        /*出錯碼*/  
  9. #include <termios.h>      /*終端參數*/  


   1.打開串口

    這里把open()這個系統調用封裝成一個com_open()函數,可以方便判斷是否打開成功並打印錯誤信息。

    參數*DEV是文件路徑(上面提到的/dev/ttyUSBx),第二個參數告訴它是以什么方式打開,常見的有:

    O_RDONLY 只讀

    O_WRONLY 只寫

    O_RDWR 讀寫

    注:上面的三個不能同時出現,即不能這樣寫O_RDONLY | O_RDWR,像下面這些是可選的:

   O_NOCTTY 如果路徑名指向終端設備,不要把這個設備用作控制終端。

   O_NONBLOCK 阻塞模式(詳見配置串口處)

    open()返回值是int型fd文件描述符,const char *DEV)  

  • {  
  •     int fd = -1;  
  •     open(DEV, O_RDWR);  
  •     if(fd == -1)  
  •     {  
  •         perror("open error");  
  •         exit(0);  
  •     }  
  •     return fd;  
  • }  

    2.配置串口

    設置串口屬性(類似於約定好雙方通信協議),即上面提到的配置串口,其實主要就是設置termios.h中的termios結構體參數:

  1. typedef struct com_attr       /*我自己定義的串口屬性結構*/           
  2. {  
  3.     unsigned int baudrate;    /*波特率*/  
  4.     unsigned char databits;    /*數據位*/  
  5.     unsigned char stopbits;    /*停止位*/  
  6.     unsigned char parity;    /*校驗位*/  
  7. }com_attr;  
  1. struct termios          /*termios結構,其實終極目的就是把我們自己定義的結構屬性設置到這里面去*/  
  2. {  
  3.    tcflag_t  c_iflag;   //輸入模式標志  
  4.    tcflag_t  c_oflag;  //輸出模式標志  
  5.    tcflag_t  c_cflag;  //控制模式標志  
  6.    tcflag_t  c_lflag;   //本地模式標志  
  7.    cc_t   c_line;              //line discipline  
  8.    cc_t   c_cc[NCC];    //control characters  
  9. }  

    可以看到兩個參數,第一個文件描述符,告訴它你想在個文件操作,第二個是我定義的串口屬性結構體:

    注:由於項目需要,可能有些不必要的參數我就沒有去設置和解釋,詳細可以google一下配置串口屬性結構體詳細介紹!

  1. int set_com_attr(int fd, com_attr *attr)  
  2. {  
  3.     struct termios opt;  
  4.     memset(&opt, 0, sizeof(struct termios));  
  5.     tcgetattr(fd, &opt);  
  6.     cfmakeraw(&opt);      
  7.       
  8.       
  9.     /*******************波特率********************/  
  10.     printf("set baudrate %d\n", attr->baudrate);  
  11.     switch (attr->baudrate)  
  12.     {  
  13.         case 50:  
  14.             cfsetispeed(&opt, B50);  
  15.             cfsetospeed(&opt, B50);  
  16.             break;  
  17.         case 75:  
  18.             cfsetispeed(&opt, B75);  
  19.             cfsetospeed(&opt, B75);  
  20.             break;  
  21.         case 110:  
  22.             cfsetispeed(&opt, B110);  
  23.             cfsetospeed(&opt, B110);  
  24.             break;  
  25.         case 134:  
  26.             cfsetispeed(&opt, B134);  
  27.             cfsetospeed(&opt, B134);  
  28.             break;  
  29.         case 150:  
  30.             cfsetispeed(&opt, B150);  
  31.             cfsetospeed(&opt, B150);  
  32.             break;  
  33.         case 200:  
  34.             cfsetispeed(&opt, B200);  
  35.             cfsetospeed(&opt, B200);  
  36.             break;  
  37.         case 300:  
  38.             cfsetispeed(&opt, B300);  
  39.             cfsetospeed(&opt, B300);  
  40.             break;  
  41.         case 600:  
  42.             cfsetispeed(&opt, B600);  
  43.             cfsetospeed(&opt, B600);  
  44.             break;  
  45.         case 1200:  
  46.             cfsetispeed(&opt, B1200);  
  47.             cfsetospeed(&opt, B1200);  
  48.             break;  
  49.         case 1800:  
  50.             cfsetispeed(&opt, B1800);  
  51.             cfsetospeed(&opt, B1800);  
  52.             break;  
  53.         case 2400:  
  54.             cfsetispeed(&opt, B2400);  
  55.             cfsetospeed(&opt, B2400);  
  56.             break;  
  57.         case 4800:  
  58.             cfsetispeed(&opt, B4800);  
  59.             cfsetospeed(&opt, B4800);  
  60.             break;  
  61.         case 9600:  
  62.             cfsetispeed(&opt, B9600);  
  63.             cfsetospeed(&opt, B9600);  
  64.             break;  
  65.         case 19200:  
  66.             cfsetispeed(&opt, B19200);  
  67.             cfsetospeed(&opt, B19200);  
  68.             break;  
  69.         case 38400:  
  70.             cfsetispeed(&opt, B38400);  
  71.             cfsetospeed(&opt, B38400);  
  72.             break;  
  73.         case 57600:  
  74.             cfsetispeed(&opt, B57600);  
  75.             cfsetospeed(&opt, B57600);  
  76.             break;  
  77.         case 115200:  
  78.             cfsetispeed(&opt, B115200);  
  79.             cfsetospeed(&opt, B115200);  
  80.             break;  
  81.         case 230400:  
  82.             cfsetispeed(&opt, B230400);  
  83.             cfsetospeed(&opt, B230400);  
  84.             break;  
  85.         case 460800:  
  86.             cfsetispeed(&opt, B460800);  
  87.             cfsetospeed(&opt, B460800);  
  88.             break;  
  89.         case 500000:  
  90.             cfsetispeed(&opt, B500000);  
  91.             cfsetospeed(&opt, B500000);  
  92.             break;  
  93.         case 576000:  
  94.             cfsetispeed(&opt, B576000);  
  95.             cfsetospeed(&opt, B576000);  
  96.             break;  
  97.         case 921600:  
  98.             cfsetispeed(&opt, B921600);  
  99.             cfsetospeed(&opt, B921600);  
  100.             break;  
  101.         case 1000000:  
  102.             cfsetispeed(&opt, B1000000);  
  103.             cfsetospeed(&opt, B1000000);  
  104.             break;  
  105.         case 1152000:  
  106.             cfsetispeed(&opt, B1152000);  
  107.             cfsetospeed(&opt, B1152000);  
  108.             break;  
  109.         case 1500000:  
  110.             cfsetispeed(&opt, B1500000);  
  111.             cfsetospeed(&opt, B1500000);  
  112.             break;  
  113.         case 2000000:  
  114.             cfsetispeed(&opt, B2000000);  
  115.             cfsetospeed(&opt, B2000000);  
  116.             break;  
  117.         case 2500000:  
  118.             cfsetispeed(&opt, B2500000);  
  119.             cfsetospeed(&opt, B2500000);  
  120.             break;  
  121.         case 3000000:  
  122.             cfsetispeed(&opt, B3000000);  
  123.             cfsetospeed(&opt, B3000000);  
  124.             break;  
  125.         case 3500000:  
  126.             cfsetispeed(&opt, B3500000);  
  127.             cfsetospeed(&opt, B3500000);  
  128.             break;  
  129.         case 4000000:  
  130.             cfsetispeed(&opt, B4000000);  
  131.             cfsetospeed(&opt, B4000000);  
  132.             break;  
  133.         default:  
  134.             printf("unsupported baudrate %d\n", attr->baudrate);  
  135.             return FALSE;  
  136.             break;  
  137.     }  
  138.   
  139.   
  140.     /************************校驗位************************/  
  141.     switch (attr->parity)  
  142.     {  
  143.         case COMM_NOPARITY:       
  144.             opt.c_cflag &= ~PARENB;   
  145.             opt.c_iflag &= ~INPCK;    
  146.             break;  
  147.         case COMM_ODDPARITY:          
  148.             opt.c_cflag |= PARENB;    
  149.             opt.c_cflag |= PARODD;    
  150.             opt.c_iflag |= INPCK;     
  151.             break;  
  152.         case COMM_EVENPARITY:         
  153.             opt.c_cflag |= PARENB;    
  154.             opt.c_cflag &= ~PARODD;   
  155.             opt.c_iflag |= INPCK;     
  156.         default:  
  157.             printf("unsupported parity %d\n", attr->parity);  
  158.             return FALSE;  
  159.             break;  
  160.     }  
  161.             opt.c_cflag &= ~CSIZE;       /*無論設置多少校驗位都需要的*/  
  162.       
  163.   
  164.         /*******************數據位*****************/  
  165.     switch (attr->databits)                
  166.     {  
  167.         case 5:  
  168.             opt.c_cflag |= CS5;  
  169.             break;  
  170.         case 6:  
  171.             opt.c_cflag |= CS6;  
  172.             break;  
  173.         case 7:  
  174.             opt.c_cflag |= CS7;  
  175.             break;  
  176.         case 8:  
  177.             opt.c_cflag |= CS8;  
  178.             break;  
  179.         default:  
  180.             printf("unsupported data bits %d\n", attr->databits);  
  181.             return FALSE;  
  182.             break;  
  183.     }  
  184.     opt.c_cflag &= ~CSTOPB;  
  185.   
  186.   
  187.     /*******************停止位***************/  
  188.     switch (attr->stopbits)         
  189.     {  
  190.         case COMM_ONESTOPBIT:  
  191.             opt.c_cflag &= ~CSTOPB;  
  192.             break;  
  193.   
  194.         case COMM_TWOSTOPBITS:  
  195.             opt.c_cflag |= CSTOPB;  
  196.             break;  
  197.         default:  
  198.             printf("unsupported stop bits %d\n", attr->stopbits);  
  199.             return FALSE;  
  200.             break;  
  201.     }  
  202.   
  203.   
  204.     /*等待時間,阻塞模式下設置的*/  
  205.     //opt.c_cc[VTIME]   = 0;    /*設置超時時間*/  
  206.     //opt.c_cc[VMIN]    = 1;      
  207.   
  208.   
  209.     opt.c_iflag &= ~(ICRNL | INLCR);  
  210.     opt.c_iflag &= ~(IXON | IXOFF | IXANY);/*關閉軟件流控(一般都是關閉軟硬流控,我也不知道為啥)*/  
  211.   
  212.   
  213.     tcflush(fd, TCIOFLUSH);        //刷清緩沖區  
  214.     if (tcsetattr(fd, TCSANOW, &opt) < 0)  
  215.     {  
  216.         printf("tcsetattr faild\n");  
  217.         return FALSE;  
  218.     }  
  219.     return TRUE;  
  220. }  

    當以阻塞模式打開時也可以通過修改結構體termios來改變位非阻塞模式或者通過函數fcntl()函數:

    阻塞:fcntl(fd, F_SETFL, 0)

    對於read,阻塞指當串口輸入緩沖區沒有數據的時候,read函數將會阻塞在這里,直到串口輸入緩沖區中有數據可讀取時read讀到了需要的字節數之后,返回值為讀到的字節數;對於write,指當串口輸出緩沖區滿或剩下的空間小於將要寫入的字節數,write函數將阻塞在這里,一直到串口輸出緩沖區中剩下的空間大於等於將要寫入的字節數,執行寫入操作,返回寫入的字節數。

    注:控制符VTIME定義要等待的時間t(百毫秒),VMIN定義了要等待的最小字節數n,以下幾種情況:
    VTIME=0,VMIN=n,read必須在讀取了VMIN個字節的數據或者收到一個信號才會返回。
    VTIME=t,VMIN=0,不管能否讀取到數據,read也要等待VTIME的時間量。
    VTIME=t,VMIN=n,那么將從read讀取第一個字節的數據時開始計時,並會在讀取到VMIN個字節或者VTIME時間后返回。
    VTIME=0,VMIN=0,不管能否讀取到數據,read都會立即返回。

    非阻塞的定義:fcntl(fd, F_SETFL,FNDELAY)

    當串口輸入緩沖區沒有數據的時候,read函數立即返回,返回值為0。

  1. void get_com_attr(int fd)  
  2. {  
  3.     struct termios opt;  
  4.     if(fd < 0)  
  5.     {  
  6.         printf("get_com_attr error");  
  7.         exit(0);  
  8.     }  
  9.     memset(&opt, 0, sizeof(struct termios));  
  10.     tcgetattr(fd, &opt);  
  11.     cfmakeraw(&opt);  
  12. }   

    有必要說一下int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)

                            int tcgetattr(int fd, struct termios *termios_p)

    tcgetattr函數用於獲取與終端相關的參數。參數fd為終端的文件描述符,結果保存在termios結構體中。

    tcsetattr函數用於設置終端的相關參數。參數optional_actions用於控制修改起作用的時間,而結構體termios_p中保存了要修改的參數;

    optional_actions可以取如下的值:
    TCSANOW:不等數據傳輸完畢就立即改變屬性。
    TCSADRAIN:等待所有數據傳輸結束才改變屬性。
    TCSAFLUSH:清空輸入輸出緩沖區才改變屬性。

   3.讀取串口

    這里也是將read()系統調用封裝成com_read(),當我們設置好通訊協議了(串口屬性),就可以對串口進行讀寫了。

    參數一fd就不用說了,第二個參數read_buff從名字看出就是要把數據讀到這個緩沖區中,第三個參數是你想要讀多少字節,注意是”你想要“,而返回值則是讀到的真正字節數,當你讀到末尾(假如緩沖區有10個字節,而你想要讀20個)或者出現異常中斷了讀操作,就會出現返回值ret(return) != nbytes。

  1. int com_read(int fd, unsigned char *read_buff, unsigned int nbytes)  
  2. {  
  3.     int ret;  
  4.     if(fd < 0)  
  5.     {  
  6.         printf("com_read error");  
  7.         exit(0);  
  8.     }  
  9.         ret = read(fd, read_buff, nbytes);  
  10.     return ret;  
  11. }  

    4.寫入串口

    道理和寫差不多

  1. int com_write(int fd, BYTE *write_buff, DWORD nbytes)  
  2. {  
  3.     int ret;  
  4.     if(fd < 0)  
  5.     {  
  6.         printf("com_write error");  
  7.         exit(0);  
  8.     }  
  9.     ret = write(fd, write_buff, nbytes);  
  10.     return ret;  
  11. }  

    5.關閉串口

    記得每次操作完串口要關閉串口(當然了,當你操作多個文件時可別操作錯了文件描述符,那就gg了)

  1. void com_close(int fd)  
  2. {  
  3.     if(fd < 0)  
  4.     {  
  5.         printf("com_close error");  
  6.         exit(0);  
  7.     }  
  8.     close(fd);  
  9. }  


    好了,萬事具備,下面就可以插上設備刷卡讀卡號啦(注意看清你的設備ttyUSBx中的x是多少啊),具體讀卡號函數就看大家的具體需求啦。

    由於博主的項目需求是要將卡號變成一個字符串然后再填充到另一個字符串,然后再巴拉巴拉,可是這個卡號讀出來是一串16進制數據,所以想了半天決定用類型轉換(不過聽說可以用fprintf)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM