Uso da função de seleção da porta serial

A função Select ainda é relativamente importante na programação de Socket, mas para aqueles que são novos no Socket, eles não gostam de escrever programas com Select.Eles são usados ​​apenas para escrever programas de bloqueio, como conectar, aceitar, recv e recvfrom.
No entanto, o uso de Select pode concluir um programa que funciona de maneira não bloqueadora.Ele pode monitorar as mudanças nos descritores de arquivo que precisamos para monitorar leitura e gravação ou anormais.

O chamado bloqueio de modo de bloqueio, como o nome indica, é que o processo ou thread deve aguardar a ocorrência de um evento quando essas funções são executadas. Se o evento não ocorrer, o processo ou thread será bloqueado e a função não poderá retornar imediatamente.
O chamado método non-blocking non-block significa que o processo ou thread não precisa esperar o evento ocorrer ao executar esta função. Uma vez que a execução retorna, a execução da função é refletida pela diferença no valor de retorno .Se o evento ocorrer, é igual ao método de bloqueio .Se o evento não ocorrer, um código é retornado para informar que o evento não ocorreu, e o processo ou thread continua em execução, portanto a eficiência é maior.
Explicação detalhada e análise de caso da função de seleção

Uso da função de seleção e análise de caso

Arquivo de cabeçalho

#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
OR
#include <sys/ioctl.h>
  •  

Definir função

int select(int n, fd_set * readfds,fd_set * writefds, fd_set * exceptfds, struct timeval * timeout);     
// select函数会不断修改timeout的值,所以每次循环都应该重新赋值

A descrição da função
select () é usada para aguardar a mudança do status do descritor de arquivo. O parâmetro n representa o maior descritor de arquivo mais 1. Os parâmetros readfds, writefds e exceptfds são chamados de frases descritivas, que são usadas para retornar as condições de leitura, gravação ou exceção do descritor.

As macros a seguir fornecem maneiras de lidar com essas três frases de descrição:

FD_CLR(inr fd,fd_set* set);-----------用来清除描述词组 set 中相关 fd 的位
FD_ISSET(int fd,fd_set *set);---------用来测试描述词组 set 中相关 fd 的位是否为真
FD_SET(int fd,fd_set*set);------------用来设置描述词组 set 中相关 fd 的位  //添加描述符
FD_ZERO(fd_set *set); ---------------用来清除描述词组 set 的全部位  //每次循环都要清空集合,否则不能检测描述符变化
  • O parâmetro timeout é a estrutura timeval, que é utilizada para definir o tempo de espera de select (). Sua estrutura é definida da seguinte forma:
struct timeval
{
	time_t tv_sec;
	time_t tv_usec;
};

struct timeval * timeout é o período de timeout de select, este parâmetro é muito importante, pode fazer select em três estados:

  • Primeiro, se NULL é passado como um parâmetro formal, isto é, nenhuma estrutura de tempo é passada, ou seja, select é colocado em um estado de bloqueio e deve esperar até que um descritor de arquivo no conjunto de descritores de arquivo de monitoramento mude;
  • Em segundo lugar, se o valor do tempo for definido como 0 segundos e 0 milissegundos, torna-se uma função pura sem bloqueio, não importa se o descritor do arquivo foi alterado, ele retornará imediatamente para continuar a execução, o arquivo retornará 0 se não houver mudança, e um valor positivo se houver mudança ;
  • Terceiro, o valor do tempo limite é maior que 0, que é o tempo limite de espera, ou seja, seleciona blocos dentro do tempo limite e retorna quando um evento chega dentro do tempo limite, caso contrário, deve retornar após o tempo limite e o retorno o valor é igual ao anterior.

O seguinte é um fragmento de programa comum

fs_set readset;
FD_ZERO(&readset);
FD_SET(fd,&readset);
select(fd+1,&readset,NULL,NULL,NULL);
if(FD_ISSET(fd,readset)		{......}

Erro de código

  • Se a execução for bem-sucedida, ele retornará o número de status alterados do descritor de arquivo;
  • Se retornar 0, significa que o tempo limite passou antes que o status do descritor mudasse;
  • Quando ocorre um erro, ele retorna -1. A causa do erro é armazenada em errno. Nesse momento, os valores dos parâmetros readfds, writefds, exceptfds e
    timeout tornam-se imprevisíveis.

Compreensão aprofundada da função de seleção

A chave para uma compreensão profunda do modelo de seleção é entender fd_set. Para a conveniência da explicação, consideramos o comprimento de fd_set como 1 byte, e cada bit em fd_set pode corresponder a um descritor de arquivo fd. Então, um fd_set longo de 1 byte pode corresponder a 8 fd no máximo.

  1. Execute fd_set set; FD_ZERO (& set); então set é expresso em bits como 0000,0000.
  2. Se fd = 5, depois de executar FD_SET (fd, & set), set torna-se 0001, 0000 (a quinta posição é 1)
  3. Se fd = 2 e fd = 1 forem adicionados novamente, o conjunto torna-se 0001,0011
  4. Execute select (6, & set, 0,0,0) para bloquear a espera
  5. Se ocorrer um evento legível em fd = 1 e fd = 2, selecione retorna e o conjunto torna-se 0000, 0011 neste momento. Quando nenhum evento legível ocorre, fd = 5
    é limpo.

Uso da função de seleção da porta serial

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //文件控制定义  
#include <termios.h>//终端控制定义  
#include <errno.h>
char* device="/dev/ttyUSB0";

int serial_fd = 0;

struct termios options;

//default config with fd--ycc1
void defaultConfigWithFd()
{
    //串口主要设置结构体termios <termios.h>
    //struct termios options;

    /**1. tcgetattr函数用于获取与终端相关的参数。
    *参数fd为终端的文件描述符,返回的结果保存在termios结构体中
    */
    tcgetattr(serial_fd, &options);
    /**2. 修改所获得的参数*/
    options.c_cflag |= (CLOCAL | CREAD);//设置控制模式状态,本地连接,接收使能
    options.c_cflag &= ~CSIZE;//字符长度,设置数据位之前一定要屏掉这个位
    options.c_cflag &= ~CRTSCTS;//无硬件流控
    options.c_cflag |= CS8;//8位数据长度
    options.c_cflag &= ~CSTOPB;//1位停止位
    options.c_iflag |= IGNPAR;//无奇偶检验位
    options.c_oflag = 0; //输出模式
    options.c_lflag = 0; //不激活终端模式
    cfsetospeed(&options, B115200);//设置波特率

    /**3. 设置新属性,TCSANOW:所有改变立即生效*/
    tcflush(serial_fd, TCIFLUSH);//溢出数据可以接收,但不读
    tcsetattr(serial_fd, TCSANOW, &options);
}
//open serial--ycc1
int openSerial(char* device)
{
    serial_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_fd < 0) {
        perror("open err");
        return -1;
    }
    return serial_fd;
}

//close
void uart_close()
{
    close(serial_fd);
}

//打开串口并初始化设置  
int init_serial(char* device)
{
    if (openSerial(device) < 0) {
        perror("open error");
        return -1;
    }
    defaultConfigWithFd();
    return 0;
}

//***ycc --bb
//set baudrate
void set_baudrate(int speed) {
    int i;
    int speed_arr[] = {B38400, B19200, B9600, B4800, B2400, B1200, B300,
                       B38400, B19200, B9600, B4800, B2400, B1200, B300};
    int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400,
                      1200, 300};
    //设置串口输入波特率和输出波特率
    for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
        if (speed == name_arr[i]) {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
        }
    }
}
//set device
void set_device(char* device)
{
    uart_close();
    serial_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
}
//set data bits
int set_data_bits(int databits)
{
    //设置数据位
    options.c_cflag &= ~CSIZE; //屏蔽其他标志位
    switch (databits)
    {
        case 5    :
            options.c_cflag |= CS5;
            break;
        case 6    :
            options.c_cflag |= CS6;
            break;
        case 7    :
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            fprintf(stderr,"Unsupported data size/n");
            return -1;
    }
}
//set flow control
void set_flow_ctrl(int flow_ctrl)
{
    //设置数据流控制
    switch(flow_ctrl)
    {

        case 0 ://不使用流控制
            options.c_cflag &= ~CRTSCTS;
            break;

        case 1 ://使用硬件流控制
            options.c_cflag |= CRTSCTS;
            break;
        case 2 ://使用软件流控制
            options.c_cflag |= IXON | IXOFF | IXANY;
            break;
    }
}
//set parity bits
void set_parity_bits(int parity)
{
    //设置校验位
    switch (parity)
    {
        case 'n':
        case 'N': //无奇偶校验位。
            options.c_cflag &= ~PARENB;
            options.c_iflag &= ~INPCK;
            break;
        case 'o':
        case 'O'://设置为奇校验    
            options.c_cflag |= (PARODD | PARENB);
            options.c_iflag |= INPCK;
            break;
        case 'e':
        case 'E'://设置为偶校验  
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= INPCK;
            break;
        case 's':
        case 'S': //设置为空格 
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported parity/n");
    }
}
//set stop bits
void set_stop_bits(int stopbits)
{
    // 设置停止位 
    switch (stopbits)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported stop bits/n");
    }
}
//***ycc --ee

/** 
*串口发送数据 
*@fd:串口描述符 
*@data:待发送数据 
*@datalen:数据长度
*/
int uart_send(char *data, int datalen)
{
    int len = 0;
    len = write(serial_fd, data, datalen);//实际写入的长度
    if(len == datalen) {
        return len;
    } else {
        tcflush(serial_fd, TCOFLUSH);//TCOFLUSH刷新写入的数据但不传送
        return -1;
    }

    return 0;
}

/**
*串口接收数据
*要求启动后,在pc端发送ascii文件
*/
int uart_recv(char *data, int datalen)
{
    int len=0, ret = 0;
    fd_set fs_read;
    struct timeval tv_timeout;

    FD_ZERO(&fs_read);
    FD_SET(serial_fd, &fs_read);
    tv_timeout.tv_sec  =2;     // (10*20/115200+5);
    tv_timeout.tv_usec = 0;

    while(FD_ISSET(serial_fd,&fs_read))
    {

        FD_ZERO(&fs_read);
        FD_SET(serial_fd,&fs_read);
        ret = select(serial_fd+1, &fs_read, NULL, NULL, &tv_timeout);
        printf("ret = %d\n", ret);
        //如果返回0,代表在描述符状态改变前已超过timeout时间,错误返回-1

        if(FD_ISSET(serial_fd, &fs_read)) {
            len = read(serial_fd, data, datalen);
            printf("len = %d\n", len);
            if(-1==len) {
                return -1;
            }
        } else {
            perror("select");
        }
    }
    return 0;
}

void testSend()
{
    char buf[]="yccyccyccy";
    init_serial("/dev/tty");

    uart_send(buf, sizeof(buf));
}
void testRsv()
{
    init_serial("/dev/tty");
    char buf1[11];
    uart_recv(buf1, sizeof(buf1));
    printf("uart receive %s\n", buf1);
}
int main(int argc, char **argv)
{
    testSend();
    //testRsv();
    return 0;
}

 

Acho que você gosta

Origin blog.csdn.net/star871016/article/details/108550363
Recomendado
Clasificación