在 C++ 中撰寫 rs232 程式 [論壇 - Ubuntu 程式設計]
正在瀏覽:
1 名遊客
在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員一級
![]() ![]() 註冊日期:
2013/9/12 9:18 所屬群組:
已註冊使用者 等級: 3
HP : 0 / 57
![]() |
請教各位前輩,
我想要在 linux 中用 c++ 撰寫程式,用 rs232 跟裝置通訊(9600, 8N1),該裝置在 windows 下測試是沒問題的,但在 linux 中就會發生資料收不完整,或是收太多的情況。 每次發送資料的長度不一定,但每筆會有一點時間差(windows 下設約 20ms 就夠了),目前發送的資料大概是 11 bytes, 109 bytes, 11 bytes, 109 bytes... 以下是我的程式: #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> #include <sys/ioctl.h> int main() { int fd; struct termios options; fd = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY); if(fd == -1) { perror("open_port: Unable to open /dev/ttyAMA0"); return 1; } tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag &= ~CRTSCTS; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_iflag &= ~INPCK; options.c_iflag &= ~(IXON | IXOFF | IXANY); options.c_oflag &= ~OPOST; options.c_cc[VMIN] = 200; options.c_cc[VTIME] = 1; // time base 0.1s tcsetattr(fd, TCSANOW, &options); char buf[255]; int res; int maxfd = fd + 1; struct timeval timeout = {1, 20000}; //20 ms fd_set readSet; while(1) { FD_ZERO(&readSet); FD_SET(fd, &readSet); if(select(maxfd, &readSet, NULL, NULL, &timeout) > 0) { if (FD_ISSET(fd, &readSet)) { res = read(fd, buf, sizeof(buf)); if(res > 0) { //Bytes received printf("read %d bytes:\n", res); for(int i=0; i<res; i++) printf("0x%02X, ", buf[i]); printf("\n"); printf("--------------------------------\n"); } } } } close(fd); return 0; } 還請各位前輩指導還有什麼地方可以改進的,謝謝
2015/3/20 22:59
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員二級
![]() ![]() 註冊日期:
2013/11/7 9:24 所屬群組:
已註冊使用者 等級: 8
HP : 0 / 187
![]() |
分成兩個部份:
1/RS232 initial 以下為可正常動作的initial:(宣告部份未列出,主要供你比對差異) if (0 > (miHandle = open(ptParam->caDeviceName, O_RDWR))) { D_PRINT("open device fail-%s-[%d]", ptParam->caDeviceName, miHandle);SHOW_LAST_ERR(1001); return -ERR_OPEN_FILE; } bzero(&termOptions, sizeof(termOptions)); // Get the current options: tcgetattr(miHandle, &save_term); termOptions = save_term; termOptions.c_cflag &= ~CSIZE; // 8bit termOptions.c_cflag |= ptParam->tDataBit; //CS8; // N termOptions.c_cflag &= ptParam->tParityCheck; //~PARENB; /* Clear parity enable */ termOptions.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); termOptions.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); // stop 1 termOptions.c_cflag &= ptParam->tStopBit; //~CSTOPB; termOptions.c_oflag = 0; termOptions.c_oflag &= ~OPOST; termOptions.c_cc[VTIME] = 0; termOptions.c_cc[VMIN] = 0; /*Baud Rate Setup for Both Input and Output*/ cfsetispeed(&termOptions, ptParam->tBaudRate); cfsetospeed(&termOptions, ptParam->tBaudRate); // Now set the term options (set immediately) if (0 > tcsetattr(miHandle, TCSANOW, &termOptions)) { D_PRINT("Setup UART fail.\n");SHOW_LAST_ERR(1002); return -ERR_UART_SETUP; } tcflush(miHandle, TCIOFLUSH); 2/接收資料 這一行 printf("0x%02X, ", cBuf[i]); 中的cBuf,沒有看到宣告,是否筆誤還是除錯的程式碼真的有錯,但是接收是對的? 基本上就算用115200 bps傳送,也不會有有20ms這樣的限制,你的程程中timeout = {1, 20000};這個東西只是在20ms內沒有讀到資料會返回,並不是20ms讀一次。除非你的主程式忙其它的事沒去讀,導致input buffer真的被塞爆了,不然應該沒有這樣的機會。
2015/3/23 18:55
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員二級
![]() ![]() 註冊日期:
2013/11/7 9:24 所屬群組:
已註冊使用者 等級: 8
HP : 0 / 187
![]() |
忘了提
資料傳送/接收的概念並不是全部傳完,再全部接收,因為你永遠不會知道要多久可以傳得完,收得完,所以用delay的方式是錯的。 正確的作法是在資料封包中加入封包頭/尾/長度/checksum等結構,才能依不同長度進行接收,不必每收完一筆才能再傳下一筆,也不需要一次就要全部收完。透過合理的封包定義,可以很有彈性的分次收,再加以組合成完整封包即可。 不知這樣的說明,你是不是能了解。
2015/3/23 21:24
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員一級
![]() ![]() 註冊日期:
2013/9/12 9:18 所屬群組:
已註冊使用者 等級: 3
HP : 0 / 57
![]() |
感謝您的回覆!!
1. 關於 cBuf 的問題的確是貼的時候筆誤,已修正 2. 您貼的程式似乎是終端機用的程式?我的裝置是純粹丟資料而已 3. 關於 20ms 的部份,因為在 windows 上寫程式時,並不需要特別去設定什麼參數,只要把 timeout 設好,不用特別寫程式,抓進來的資料就會是正確的一筆一筆的樣子。我想應該是利用每筆資料傳送時有個時間差,來判斷什麼時候是一筆完整的資料(這邊假設傳輸都沒問題,資料沒有遺失的情況下,如果有遺失,那的確是要將資料全部收下來,再根據封包結構去檢查、切割資料) 所以我想說既然 windows 可以,linux 應該也可以才是,不過還沒試出來... 想用 c_cc[VTIME] 去控制,但這個最小單位是 100ms,似乎無法再小了
2015/3/24 22:35
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員二級
![]() ![]() 註冊日期:
2013/11/7 9:24 所屬群組:
已註冊使用者 等級: 8
HP : 0 / 187
![]() |
2/ 您貼的程式似乎是終端機用的程式?我的裝置是純粹丟資料而已
-> 坦白說,也沒有所謂的終端機或其它,純粹就是一個可以收、發的小程式,我拿它來對MCU、Linux、Windows都是一樣的東西。 3/ 由於永遠不會知道資料長度變動的情況是如何,所以用delay真的不是好的方法。由你的程式來,你每20ms收下來的資料可能不夠長,那只是因為在那當下資料還沒送完,下一次再讀,就可以把剩下的讀回來,最終再把封包組合分析即可。你只提到分析封包,但是沒有考慮到組合封包,在兩邊非同步的情況下,很難保證每次讀都是完整的內容。 舉個例來說,A送B收,若先啟動B,再執行A,我想你的夢想可能有機會成真,但只是有可能;若今天先啟動A並開始送,在執行B,那結果如何?你根本不會有完整資料的一天,因為你無從分辨那個地方開始,那個地方結束。甚至只要任何一個地方發生干擾,少了1Byte或多了1Byte,接下來你所有的內容就不可能會是正確的了,因為你找不到下一個開始點在什麼地方?不知這樣是否對你有幫助!
2015/3/25 13:05
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員一級
![]() ![]() 註冊日期:
2013/9/12 9:18 所屬群組:
已註冊使用者 等級: 3
HP : 0 / 57
![]() |
嗯,您說的也是有道理,那我瞭解了,會朝這方面去試試的,謝謝您撥空回覆!!
2015/3/25 21:58
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員三級
![]() ![]() 註冊日期:
2016/4/18 12:29 所屬群組:
已註冊使用者 等級: 9
HP : 0 / 221
![]() |
偶然看到這個主題
請問裝置為什麼是AMA,一般不是ttyS0嗎? fd = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY); 謝謝
2018/3/20 16:40
|
||||||||||
![]() |
回覆: 在 C++ 中撰寫 rs232 程式 |
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
會員二級
![]() ![]() 註冊日期:
2013/11/7 9:24 所屬群組:
已註冊使用者 等級: 8
HP : 0 / 187
![]() |
ubuntu_net2016 寫到: 這個名稱是由driver決定的,不是固定的。簡單的說,該設備的driver中會指定此設備的名稱,而ttySX是內鍵driver的名稱,其中的X可以看作是流水號。
2018/3/22 9:34
|
||||||||||
![]() |
您可以查看帖子.
您不可發帖.
您不可回覆.
您不可編輯自己的帖子.
您不可刪除自己的帖子.
您不可發起投票調查.
您不可在投票調查中投票.
您不可上傳附件.
您不可不經審核直接發帖.