Tôi đã đọc các liên kết được theo dõi và các nguồn khác, nhưng không tìm thấy câu trả lời cho câu hỏi của tôi.Làm thế nào để đọc dữ liệu nhị phân trên thiết bị đầu cuối nối tiếp trong chương trình C?
Binary data over serial terminal
Data gets corrupted during transmission over the serial port
tôi giao tiếp với thiết bị nhúng của mình thông qua một cổng nối tiếp. Theo mặc định, Linux nhúng sử dụng cổng này làm thiết bị đầu cuối. Nhưng tôi cũng muốn truyền dữ liệu nhị phân (các gói dịch vụ) thông qua cổng. File/etc/inittab của tôi có một "getty" gọi: console :: respawn:/sbin/Getty 115200 ttyS0
Tôi cũng có file/etc/passwd với chuỗi nơi "admin" user khởi động của tôi "cli "ứng dụng sau khi đăng nhập: admin: 8Mt/Jtxcyg8AY: 1000: 0: admin:/tmp:/tmp/cli
thiết lập ttyS0 mặc định của tôi trước khi chạy chương trình là:
~ # stty -a
speed 115200 baud;stty: standard input
line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ^J;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon -iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
-echoctl echoke
~ #
Vì vậy, , trong chương trình cli của tôi, tôi làm như sau:
main()
{
...
system("stty erase ^H);
system("stty -F /dev/ttyS0 -icrnl -ixon -ixoff -opost -isig -icanon -echo"); // enter in non-canonical (raw) mode
// What function do I need to use here to retrieve binary data (also symbols that > 0x7F) from /dev/ttyS0?
system("stty -F /dev/ttyS0 icrnl ixon ixoff opost isig icanon echo"); // go back to canonical mode
...
exit(0);
}
Tôi đã thử hàm read() (với bộ đệm char chưa ký) để nhận dữ liệu nhị phân nhưng không nhận được dữ liệu chính xác. Tôi cũng mở sơ bộ/dev/ttyS0 lần nữa để lấy file_descriptor & sử dụng read() func.
Chương trình của tôi gửi 3 byte: 0xAA, 0x02, 0xFE. Nhưng trong syslog tôi luôn thấy rằng thiết bị nhận được các ký hiệu không đúng: 0x98, 0xE6, 0x18.
Vấn đề là gì? Làm thế nào để có được dữ liệu nhị phân chính xác?
Toàn bộ mã mà tôi đang thử nghiệm tại thời điểm này.
#include "cli.h"
#include "glb_vars.h"
/******************************************
*** Definitions
******************************************/
#define APPLICATION_NAME "cli"
#define SERIALPORT_IS_CONSOLE
/******************************************
*** Constants
******************************************/
const char dev_name[] = DEV_NAME;
const char lineminstr[] = "\t--------------------------------------------------------\n";
/******************************************
*** Internal Function Declarations
******************************************/
CLI_RETVAL cliInit(void);
CLI_RETVAL cliClose(void);
void cliWorkLoop(Term_callback_t **term);
/******************************************
*** External Function Declarations
******************************************/
extern void Vectors_init(Term_callback_t **vec);
extern char** Menu_completion(const char * text, int start, int end);
/****************************************************************************/
int file_descr, max_fd;
struct termios tty, orig_tty;
fd_set work_set;
/****************************************************************************/
/*!
* \brief Init cli
*
* \return success or failure
* \retval CLI_SUCCESS, CLI_FAILURE
*
* \ingroup CLI
*/
/****************************************************************************/
CLI_RETVAL cliInit(void)
{
long spd;
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGABRT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGILL, SIG_IGN);
// system("stty -F /dev/ttyS0 -icrnl -ixon -ixoff -opost -isig -icanon -echo"); // enter in non-canonical mode
// system("stty -a");
// sleep(1);
#ifdef SERIALPORT_IS_CONSOLE
file_descr = STDIN_FILENO;
SYS_LOG_DEBUG("SERIALPORT IS CONSOLE");
#else
SYS_LOG_DEBUG("SERIALPORT IS NOT CONSOLE");
file_descr = open("/dev/ttyS0", O_RDWR | O_ASYNC | O_NDELAY);
if (file_descr == -1) {
// Could not open the port
perror("unable to open /dev/ttyS0");
exit(1);
}
#endif
if(tcgetattr(file_descr, &tty) < 0)
{
perror("unable to get tty attributes");
exit(1);
}
// backup tty, make it raw and apply changes
orig_tty = tty;
spd = B115200;
cfsetospeed(&tty, (speed_t)spd);
cfsetispeed(&tty, (speed_t)spd);
cfmakeraw(&tty);
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 10;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS; /* no HW flow control? */
tty.c_cflag |= CLOCAL | CREAD;
tcsetattr(file_descr, TCSANOW, &tty);
// // update local mode flags
// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//// // renew control mode flags
//// tty.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD);
//// tty.c_cflag |= (BAUD | DATABITS | STOPBITS | PARITYON | PARITY);
// // select 'raw' output mode
// tty.c_oflag &= ~OPOST;
// // disable mapping for input mode
// tty.c_iflag &= ~(INLCR | ICRNL);
//
//
// if(tcsetattr(file_descr, TCSAFLUSH, &tty) < 0)
// {
// perror("unable to set tty attributes");
// exit(1);
// }
//
// Setup fd_set
FD_ZERO(&work_set);
FD_SET(file_descr, &work_set);
max_fd = file_descr + 1;
/* Readline lib init */
// Define application name for readline library
rl_readline_name = APPLICATION_NAME;
// Update Pointer to alternative function to create matches.
rl_attempted_completion_function = Menu_completion;
// Start readline with reading /etc/inputrc file
using_history();
stifle_history(CLI_MAX_HISTORY_SIZE);
// Some other initialization code
// ...
// ...
return CLI_SUCCESS;
}
/****************************************************************************/
/*!
* \brief Close cli
*
* \return success or failure
* \retval CLI_SUCCESS, CLI_FAILURE
*
* \ingroup CLI
*/
/****************************************************************************/
CLI_RETVAL cliClose(void)
{
// system("stty -F /dev/ttyS0 icrnl ixon ixoff opost isig icanon echo"); // enter in canonical mode
tcsetattr(file_descr, TCSANOW, &orig_tty);
// if(tcsetattr(file_descr, TCSAFLUSH, &orig_tty) < 0)
// {
// perror("unable to set orig_tty attributes");
// exit(1);
// }
close(file_descr);
return CLI_SUCCESS;
}
/****************************************************************************/
/*!
* \brief Main cli processing loop
*
* \no return
*
* \ingroup CLI
*/
/****************************************************************************/
void cliWorkLoop(Term_callback_t **term)
{
Term_callback_t *cur_term;
int8 *commandString;
uint8 ret = CLI_REFRESH, no_prompt;
char prompt_str[20];
while (1) {
cur_term = *term;
global_cmd_compl_pointer = cur_term->cmd_list;
commandString = NULL;
sprintf(prompt_str, "%s:~> ", dev_name);
if(ret == CLI_REFRESH) {
CLEAR_SCR();
if(cur_term->out != NULL) {
cur_term->out(term, commandString, &ret);
no_prompt = ret;
}
CURSOR_DOWN();
}
int n;
struct timeval timeout;
uint8 tmpBuf[32];
while (1)
{
// Setup Timeout
timeout.tv_sec = 60;
timeout.tv_usec = 0;
// Wait for new connections
n = select(max_fd, &work_set, NULL, NULL, &timeout);
if (n < 0)
{
perror("select #2 failed");
break;
}
if (n > 0)
{
/* У нас есть ввод */
if (FD_ISSET(file_descr, &work_set))
{
if (read(file_descr, tmpBuf, 10) < 0) {
perror("cannot read");
exit(1);
}
else
{
SYS_LOG_DEBUG("READ first 4 chars: 0x%X,0x%X,0x%X,0x%X", tmpBuf[0], tmpBuf[1], tmpBuf[2], tmpBuf[3]);
}
}
break;
}
}
//
//
// n = read(file_descr, tmpBuf, 5);
// if (n > 0) {
// unsigned char *p = tmpBuf;
//
// while (n-- > 0)
// printf(" 0x%x", *p++);
// printf("\r\n");
// } else {
// printf("failed to read: %d\r\n", n);
// }
//
//
exit(0);
}
CLEAR_SCR();
return;
}
/****************************************************************************/
/*!
* \brief Main cli function
*
* \param[in] argc - argument number.
* \param[in,out] argv - argument values entered by user.
*
* \return success or failure
* \retval EXIT_SUCCESS, EXIT_FAILURE
*
*
* \ingroup CLI
*/
/****************************************************************************/
int main(int argc, char *argv[])
{
Term_callback_t *term;
char logname[16];
FILE *fp;
/* Set mask for file operation */
umask(0);
system("stty erase ^H");
openlog("cli", LOG_CONS, LOG_USER);
/* Write cli start log */
syslog(LOG_NOTICE, "Console startup. Software version: %s", VERSION);
/* Find login name */
strcpy(logname, "noname");
if ((fp = popen("whoami", "r")) == NULL)
{
SYS_LOG_ERR("Can't open process for \"whoami\" command.");
} else
{
fgets(logname, 16, fp);
pclose(fp);
}
SYS_LOG_INFO("Console is entered by \"%s\".", logname); //getenv("USER")
/* Console initialization */
if (cliInit() != CLI_SUCCESS) {
SYS_LOG_CRIT("CLI init failed");
return EXIT_FAILURE;
}
Vectors_init(&term);
/* Console work loop */
cliWorkLoop(&term);
cliClose();
/* Exiting from cli */
SYS_LOG_INFO("\"%s\" exited from console.", logname);
return EXIT_SUCCESS;
}
Cổng nối tiếp của bạn được thiết lập cho đầu vào chuẩn, tức là dòng văn bản ASCII. Hoặc chuyển sang * chế độ thô * hoặc chuyển đổi dữ liệu nhị phân ở cả hai đầu. Xem 'uuencode' và 'uudecode' để truyền dữ liệu nhị phân qua môi trường ASCII. Đó là cách truyền thống để gửi dữ liệu nhị phân trong các e-mail chỉ bằng văn bản và USENET. Một phương pháp khác để gửi các ký tự nhị phân hoặc "đặc biệt" là sử dụng ký tự "thoát" vào tiền tố "đặc biệt" và chuẩn hóa byte thành một byte ASCII hợp lệ. BTW '-parenb -parodd cs8' sẽ tạo ra các khung ký tự 11 bit. "Không chẵn lẻ" là điển hình khi sử dụng các ký tự dữ liệu 8 bit. – sawdust
Cảm ơn bạn đã thấy mùn cưa! Bạn nói đúng, biến thể thứ hai là một phương pháp thuận tiện hơn để sử dụng. Nhưng tôi có một phần mềm cũ giao tiếp với các thiết bị nhúng trước đó và không hỗ trợ nó. Có khả năng chúng ta sẽ phải thay đổi nó để hỗ trợ một cái mới. Mặc dù vậy, tại sao tôi không thể có được một char đặc biệt khi đi vào một chế độ thô (vui lòng xem mã đã đề cập) bằng cách sử dụng một hàm read() func? – Bakir