营销型网站建设0469z,制作网站的模板下载软件,购物网站管理系统,绵阳做网站的公司一、poll() 函数的介绍 poll() 函数用于监控多个文件描述符的变化的函数。它可以用来检查一个或多个文件描述符的状态是否改变#xff0c;比如是否可读、可写或有错误发生。它常用于处理 I/O 多路复用#xff0c;这在需要同时处理多个网络连接或文件操作时非常有用。
头文件…一、poll() 函数的介绍 poll() 函数用于监控多个文件描述符的变化的函数。它可以用来检查一个或多个文件描述符的状态是否改变比如是否可读、可写或有错误发生。它常用于处理 I/O 多路复用这在需要同时处理多个网络连接或文件操作时非常有用。
头文件
#include poll.h 函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);fds一个指向 pollfd 结构数组的指针每个 pollfd 结构代表一个要监控的文件描述符。 nfdsfds 数组中的元素数量。 timeout等待的超时时间毫秒。如果设置为 -1则 poll() 会一直等待直到某个文件描述符的状态改变或捕获到信号。如果设置为 0则 poll() 会立即返回不会等待。 struct pollfd 结构
struct pollfd { int fd; // 文件描述符 short events; // 感兴趣的事件 short revents; // 返回的事件
};fd要监控的文件描述符。 events用户感兴趣的事件可以是以下值的组合 POLLIN数据可读 POLLOUT数据可写 POLLPRI优先级数据可读 POLLERR发生错误 POLLHUP挂起只用于流 POLLNVAL请求无效的文件描述符 revents返回时这个字段包含了实际发生的事件。 返回值 成功时返回更改状态的文件描述符数量。 如果超时返回 0。 失败时返回 -1 并设置 errno。 示例 下面是一个简单的 poll() 使用示例它监控标准输入stdin是否可读
#include stdio.h
#include stdlib.h
#include unistd.h
#include poll.h int main() { struct pollfd fds[1]; int ret; // 设置要监控的文件描述符和事件 fds[0].fd STDIN_FILENO; // 标准输入的文件描述符 fds[0].events POLLIN; // 监控读事件 // 使用 poll 监控文件描述符 ret poll(fds, 1, -1); // 无限等待直到有事件发生 if (ret -1) { perror(poll); exit(EXIT_FAILURE); } // 检查哪个文件描述符的状态发生了改变 if (fds[0].revents POLLIN) { printf(Standard input is readable.\n); // 这里可以读取标准输入的数据 } return 0;
}这个示例程序会等待用户从标准输入通常是键盘输入数据。一旦有数据可读poll() 就会返回并设置 fds[0].revents 为 POLLIN表示标准输入现在可读。然后程序会打印一条消息。注意这个示例并没有实际读取输入数据只是检测了输入是否可读。如果需要读取数据你可以使用如 read() 或 fgets() 等函数来从标准输入读取数据。
二、串口使用poll()函数测试代码 测试代码基于文章 嵌入式Linux开发板测试esp8266模块 进行修改测试文章链接如下 http://t.csdnimg.cn/y31It 修改后代码如下
#define _GNU_SOURCE
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include sys/ioctl.h
#include errno.h
#include string.h
#include signal.h
#include termios.h
#include sys/select.h
#include poll.h#define REV_OK 0 //接收完成标志
#define REV_WAIT 1 //接收未完成标志
#define UART_BUF 128 // 串口缓冲区#define ESP8266_AP_INFO ATCWSAP\ATK-8266\,\12345678\,1,4\r\n
#define ESP8266_JAP_INFO ATCWJAP\TP-LINK_xxxx\,\123456789\\r\n
#define ESP8266_TCP_SERVER_INFO ATCIPSTART\TCP\,\192.168.1.182\,8080\r\n#define u8 unsigned charunsigned short esp8266_cnt 0, esp8266_cntPre 0;
unsigned char buf[UART_BUF] {0};/* 实现ms级延时 */
static void delay_xms(unsigned int secs)
{struct timeval tval;tval.tv_secsecs/1000;tval.tv_usec(secs*1000)%1000000;select(0,NULL,NULL,NULL,tval);
}typedef struct uart_hardware_cfg {unsigned int baudrate; /* 波特率 */unsigned char dbit; /* 数据位 */char parity; /* 奇偶校验 */unsigned char sbit; /* 停止位 */
}uart_cfg_t;static struct termios old_cfg; /* 用于保存终端的配置参数 */
static int fd; /* 串口终端对应的文件描述符 *//* 串口初始化
打开串口文件描述符即对应的串口终端的设备节点 */
static int uart_init(const char *device)
{/* 打开串口 *//* O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机 */fd open(device, O_RDWR | O_NOCTTY); if (fd 0){fprintf(stderr, open error:%s:%s\n, device, strerror(errno));return -1;}/* 获取串口当前的配置参数 */if (0 tcgetattr(fd, old_cfg)) {fprintf(stderr, tcgetattr error: %s\n, strerror(errno));close(fd);return -1;}return 0;
}/**** 串口配置** 参数cfg指向一个uart_cfg_t结构体对象**/
static int uart_cfg(const uart_cfg_t *cfg)
{struct termios new_cfg {0}; //将new_cfg对象清零speed_t speed;/* 设置为原始模式 */cfmakeraw(new_cfg);/* 使能接收 */new_cfg.c_cflag | CREAD;/* 设置波特率 */switch (cfg-baudrate) {case 1200: speed B1200;break;case 1800: speed B1800;break;case 2400: speed B2400;break;case 4800: speed B4800;break;case 9600: speed B9600;break;case 19200: speed B19200;break;case 38400: speed B38400;break;case 57600: speed B57600;break;case 115200: speed B115200;break;case 230400: speed B230400;break;case 460800: speed B460800;break;case 500000: speed B500000;break;default: //默认配置为115200speed B115200;printf(default baud rate: 115200\n);break;}if (0 cfsetspeed(new_cfg, speed)) {fprintf(stderr, cfsetspeed error: %s\n, strerror(errno));return -1;}/* 设置数据位大小 */new_cfg.c_cflag ~CSIZE; //将数据位相关的比特位清零switch (cfg-dbit) {case 5:new_cfg.c_cflag | CS5;break;case 6:new_cfg.c_cflag | CS6;break;case 7:new_cfg.c_cflag | CS7;break;case 8:new_cfg.c_cflag | CS8;break;default: //默认数据位大小为8new_cfg.c_cflag | CS8;
// printf(default data bit size: 8\n);break;}/* 设置奇偶校验 */switch (cfg-parity) {case N: //无校验new_cfg.c_cflag ~PARENB;new_cfg.c_iflag ~INPCK;break;case O: //奇校验new_cfg.c_cflag | (PARODD | PARENB);new_cfg.c_iflag | INPCK;break;case E: //偶校验new_cfg.c_cflag | PARENB;new_cfg.c_cflag ~PARODD; /* 清除PARODD标志配置为偶校验 */new_cfg.c_iflag | INPCK;break;default: //默认配置为无校验new_cfg.c_cflag ~PARENB;new_cfg.c_iflag ~INPCK;
// printf(default parity: N\n);break;}/* 设置停止位 */switch (cfg-sbit) {case 1: //1个停止位new_cfg.c_cflag ~CSTOPB;break;case 2: //2个停止位new_cfg.c_cflag | CSTOPB;break;default: //默认配置为1个停止位new_cfg.c_cflag ~CSTOPB;
// printf(default stop bit size: 1\n);break;}/* 将MIN和TIME设置为0 */new_cfg.c_cc[VTIME] 0;new_cfg.c_cc[VMIN] 0;/* 清空缓冲区 */if (0 tcflush(fd, TCIOFLUSH)) {fprintf(stderr, tcflush error: %s\n, strerror(errno));return -1;}/* 写入配置、使配置生效 */if (0 tcsetattr(fd, TCSANOW, new_cfg)) {fprintf(stderr, tcsetattr error: %s\n, strerror(errno));return -1;}/* 配置OK 退出 */return 0;
}//
// 函数名称 ESP8266_Clear
//
// 函数功能 清空缓存
//
// 入口参数 无
//
// 返回参数 无
//
// 说明
//
void ESP8266_Clear(void)
{memset(buf, 0, sizeof(buf));esp8266_cnt 0;
}/**** 信号处理函数当串口有数据可读时会跳转到该函数执行**/
static void io_handler(int sig, siginfo_t *info, void *context)
{if(SIGRTMIN ! sig)return;/* 判断串口是否有数据可读 */if (POLL_IN info-si_code) {if (esp8266_cnt sizeof(buf)){// esp8266_cnt 0; //防止串口被刷爆// memset(buf,0,sizeof(buf));ESP8266_Clear();}esp8266_cnt read(fd, bufesp8266_cnt, sizeof(buf)-esp8266_cnt);printf(esp8266_cnt%d\r\n, esp8266_cnt);printf(io_handler%s\r\n, buf);}
}/**** 异步I/O初始化函数**/
static void async_io_init(void)
{struct sigaction sigatn;int flag;/* 使能异步I/O */flag fcntl(fd, F_GETFL); //使能串口的异步I/O功能flag | O_ASYNC;fcntl(fd, F_SETFL, flag);/* 设置异步I/O的所有者 */fcntl(fd, F_SETOWN, getpid());/* 指定实时信号SIGRTMIN作为异步I/O通知信号 */fcntl(fd, F_SETSIG, SIGRTMIN);/* 为实时信号SIGRTMIN注册信号处理函数 */sigatn.sa_sigaction io_handler; //当串口有数据可读时会跳转到io_handler函数sigatn.sa_flags SA_SIGINFO;sigemptyset(sigatn.sa_mask);sigaction(SIGRTMIN, sigatn, NULL);
}void Uart4_Init(unsigned int baud, char *device)
{uart_cfg_t cfg {0};if (NULL device) {fprintf(stderr, Error: the device no found!\n);exit(EXIT_FAILURE);}/* 串口初始化 */if (uart_init(device))exit(EXIT_FAILURE);/* 设置波特率 */cfg.baudrate baud;/* 串口配置 */if (uart_cfg(cfg)) {tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}
}void Uart5_Init(unsigned int baud, char *device)
{uart_cfg_t cfg {0};if (NULL device) {fprintf(stderr, Error: the device no found!\n);exit(EXIT_FAILURE);}/* 串口初始化 */if (uart_init(device))exit(EXIT_FAILURE);/* 设置波特率 */cfg.baudrate baud;/* 串口配置 */if (uart_cfg(cfg)) {tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}
}//
// 函数名称 ESP8266_WaitRecive
//
// 函数功能 等待接收完成
//
// 入口参数 char *res
//
// 返回参数 REV_OK-接收完成 REV_WAIT-接收超时未完成
//
// 说明 循环调用检测是否接收完成
//
_Bool ESP8266_WaitRecive(char *res)
{int err0,nbytes0,i0;struct pollfd fds[] {{.fd fd,.events POLLIN,},};err poll(fds, 1, 5000);//5sswitch(err){case -1://出错printf(\n poll read error %d \n, err);nbytes0;break;case 0://超时printf(\n poll read error %d \n, err);nbytes0;break;default:nbytes read(fd, bufesp8266_cnt, sizeof(buf)-esp8266_cnt);break;}esp8266_cnt nbytes;if(esp8266_cnt 0) //如果接收计数为0 则说明没有处于接收数据中所以直接跳出结束函数return REV_WAIT;if( strstr((const char *)buf, res) ! NULL || esp8266_cnt esp8266_cntPre) //如果上一次的值和这次相同则说明接收完毕{// for (i 0; i esp8266_cnt; i)// printf( 0x%02x, buf[i]);// printf(\n);printf(***********************\n);printf(%s,buf);printf(***********************\n);esp8266_cnt 0; //清0接收计数esp8266_cntPre0;return REV_OK; //返回接收完成标志}esp8266_cntPre esp8266_cnt; //置为相同return REV_WAIT; //返回接收未完成标志}int ESP8266_Recive(void)
{int err0,nbytes0,i0;struct pollfd fds[] {{.fd fd,.events POLLIN,},};memset(buf,0,sizeof(buf));err poll(fds, 1, 1000);//1sswitch(err){case -1://出错printf(\n poll read error %d \n, err);nbytes0;break;case 0://超时printf(\n poll read error %d \n, err);nbytes0;break;default:nbytes read(fd, buf, sizeof(buf));break;}if(nbytes0){return nbytes;}return -1;}//
// 函数名称 ESP8266_SendCmd
//
// 函数功能 发送命令
//
// 入口参数 cmd命令
// res需要检查的返回指令
//
// 返回参数 0-成功 1-失败
//
// 说明
//
_Bool ESP8266_SendCmd(char *cmd, char *res)
{int ret 0,err,nbytes;unsigned int timeOutCnt 3;ESP8266_Clear();ret write(fd, (unsigned char *)cmd, strlen((const char *)cmd));if (ret 0) printf(write err!\r\n);
// printf(UartBuf:%s, Len:%d\r\n, buf, esp8266_cnt);while(timeOutCnt--){if(ESP8266_WaitRecive(res) REV_OK) //如果收到数据{
// printf(ESP8266_SendCmd Function:%s\r\n, buf);if(strstr((const char *)buf, res) ! NULL) //如果接收到的数据是在给定数据的范围内则不为NULL{ESP8266_Clear(); //清空缓存return 0;}}//delay_xms(10);//10s内让串口2中断函数循环接收到的数据}return 1;}//
// 函数名称 ESP8266_SendData
//
// 函数功能 发送数据
//
// 入口参数 data数据
// len长度
//
// 返回参数 无
//
// 说明
//
void ESP8266_SendData(unsigned char *data, unsigned short len)
{char cmdBuf[32];ESP8266_Clear(); //清空接收缓存// sprintf(cmdBuf, ATCIPSEND0,%d\r\n, len); //发送命令 多连接sprintf(cmdBuf, ATCIPSEND%d\r\n, len); //发送命令 单连接if(!ESP8266_SendCmd(cmdBuf, )) //收到‘’时可以发送数据,改不得{write(fd, data, len); //发送设备连接请求数据}}_Bool Esp8266_Init()
{ESP8266_Clear();printf(1. AT\r\n);while(ESP8266_SendCmd(AT\r\n, OK))delay_xms(500);printf(2. CWMODE\r\n);while(ESP8266_SendCmd(ATCWMODE2\r\n, OK))delay_xms(500);printf(2.1 ATRST\r\n);while(ESP8266_SendCmd(ATRST\r\n, OK))delay_xms(2000);printf(3. CWSAP\r\n);while(ESP8266_SendCmd(ESP8266_AP_INFO, OK)){delay_xms(500);}printf(4. ATCIPMUX\r\n);// 启动多连接while(ESP8266_SendCmd(ATCIPMUX1\r\n, OK)){delay_xms(500);}printf(5. CIPSERVER\r\n);while(ESP8266_SendCmd(ATCIPSERVER1,8080\r\n, OK))delay_xms(500);printf(6. CIFSR\r\n);while(ESP8266_SendCmd(ATCIFSR\r\n, OK))delay_xms(500);printf(6. ESP8266 Init OK\r\n);return 0;
}
_Bool Esp8266_Init_One_TCP_Client()
{ESP8266_Clear();printf(1. CWMODE\r\n);while(ESP8266_SendCmd(ATCWMODE1\r\n, OK))delay_xms(5000);// printf(1.1 CWDHCP\r\n);// while(ESP8266_SendCmd(ATCWDHCP1,1\r\n, OK))// delay_xms(5000);printf(1.1 CIPSTA\r\n);while(ESP8266_SendCmd(ATCIPSTA\192.168.1.87\,\192.168.1.1\,\255.255.255.0\\r\n, OK))delay_xms(5000);//printf(2. CWJAP\r\n);while(ESP8266_SendCmd(ESP8266_JAP_INFO, OK))//WIFI CONNECTED ;// OKdelay_xms(5000);// printf(2.1 ATRST\r\n);// while(ESP8266_SendCmd(ATRST\r\n, OK))// delay_xms(20000);printf(3. CIFSR\r\n);while(ESP8266_SendCmd(ATCIFSR\r\n, OK))delay_xms(5000);printf(4. CIPSTART\r\n);while(ESP8266_SendCmd(ESP8266_TCP_SERVER_INFO, ERROR)0)delay_xms(5000*10);printf(4. ESP8266 Init OK\r\n);return 0;
}int main(int argc, char *argv[])
{u8 test_buf[] esp8266 $$$$$!\r\n;// Uart4_Init(115200, argv[1]); // 初始化串口4Uart5_Init(115200, argv[1]); // 初始化串口5//async_io_init(); // 异步IO初始化相当于STM32的外部中断配置的初始化,不过STM32下应该是硬件中断这里是软件中断 // while (Esp8266_Init())// {// printf(Esp8266 Init Failed!!!\r\n);// }while (Esp8266_Init_One_TCP_Client()){printf(Esp8266 Init Failed!!!\r\n);}while (1){if(ESP8266_Recive() -1){sleep(1); continue;}/* 通信测试 */if (strstr((const char*)buf, AA)){ESP8266_SendData(test_buf,strlen(test_buf));ESP8266_Clear();}else if(strstr((const char*)buf, end)){sleep(1); ESP8266_Clear();break;}}/* 退出 */tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_SUCCESS);
}三、测试结果
初始化部分
1. CWMODE
***********************
ATCWMODE1OK
***********************
1.1 CIPSTA
***********************
ATCIPSTA192.168.1.87,192.168.1.1,255.255.255.0OK
***********************
2. CWJAP
***********************
ATCWJAPTP-LINK_xxxx,123456789
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IPOK
***********************
3. CIFSR
***********************
ATCIFSR
CIFSR:STAIP,192.168.1.87
CIFSR:STAMAC,34:94:54:7f:d4:71OK
***********************
4. CIPSTARTpoll read error 0
***********************
ATCIPSTARTTCP,192.168.1.182,8080
CONNECTOK
***********************
4. ESP8266 Init OK
主函数循环接收接收到AA返回 esp8266 $$$$$!接收到end则结束。