珠海工程建设信息网站win2003 wordpress
- 作者: 五速梦信息网
- 时间: 2026年03月21日 05:04
当前位置: 首页 > news >正文
珠海工程建设信息网站,win2003 wordpress,免费自助建站软件下载,中信建设有限责任公司ipo串口应用编程 串口应用编程介绍
介绍 串口定义:串行接口,数据按顺序传输 串口特点:通信线路简单,距离远,速度较低 应用领域:常用工业接口 Linux系统中的作用 作为标准输入输出设备 系统打印信息输出 用户与系统交互 串口与终端:在Linux系统中,串口被视为一种终端#…串口应用编程 串口应用编程介绍
介绍 串口定义:串行接口,数据按顺序传输 串口特点:通信线路简单,距离远,速度较低 应用领域:常用工业接口 Linux系统中的作用 作为标准输入输出设备 系统打印信息输出 用户与系统交互 串口与终端:在Linux系统中,串口被视为一种终端Terminal设备
终端 Terminal 终端定义 处理主机输入输出的设备实现人机交互 终端分类 本地终端如PC机显示器键盘组合 串口连接的远程终端 基于网络的远程终端 物理终端vs伪终端 物理终端直接关联物理设备 本地终端如PC机显示器键盘组合 串口连接的远程终端 伪终端不直接关联物理设备 基于网络的远程终端 Linux中终端对应的设备节点 Linux终端原则一切皆文件每个终端在/dev目录下有对应设备节点 本地终端设备节点 格式/dev/ttyXX为数字编号 范围/dev/tty1 ~ /dev/tty63共63个 本地终端设备节点 特点Linux内核初始化时生成 伪终端设备节点 格式/dev/pts/XX为数字编号 伪终端设备节点 特点远程登录时生成 串口终端设备节点 示例/dev/ttymxcX基于特定开发板 对于 ALPHA/Mini I.MX6U 开发板来说有两个串口 设备节点编号说明 编号与硬件支持的串口数量和注册情况有关 出厂系统只注册了 2 个串口外设分别是 UART1 和 UART3所以对应这个数字就是 0 和 2 注意命名与硬件平台相关但通常以tty开头 查看系统连接终端 使用who命令可查看当前连接的终端 可显示本地终端、串口终端和远程登录的伪终端
串口应用编程 串口在Linux系统中的定位 属于终端设备 在特定开发板上对应/dev/ttymxc0和/dev/ttymxc2 串口应用编程基本操作 使用ioctl()配置串口 使用read()读取数据 使用write()写入数据 就是这么简单但是我们不这么做Linux 为上层用户做了一层封装 Linux提供的串口编程封装 封装了底层ioctl()操作 提供了一套标准API称为termios API termios API特点 是C库函数 可通过man手册查看帮助信息 适用于所有终端设备不仅限于串口 termios API的应用范围 串口设备 本地连接的鼠标、键盘 远程登录的伪终端 使用termios API的准备 在应用程序中包含termios.h头文件
struct termios 结构体 终端应用编程的两个主要方面 配置 读写 struct termios结构体的重要性 描述终端的配置信息 控制和影响终端的行为和特性 是终端设备应用编程的核心 struct termios结构体的组成 struct termios { tcflag_t c_iflag; /* input mode flags / tcflag_t c_oflag; / output mode flags / tcflag_t c_cflag; / control mode flags / tcflag_t c_lflag; / local mode flags / cc_t c_line; / line discipline / cc_t c_cc[NCCS]; / control characters / speed_t c_ispeed; / input speed / speed_t c_ospeed; / output speed */ }; 输入模式c_iflag 输入模式的作用 控制输入数据在传递给应用程序前的处理方式 处理来自串口或键盘的字符数据 输入模式的配置方法 通过设置struct termios结构体中的c_iflag成员 使用预定义的宏来设置标志 获取详细信息的方法 可以通过man手册查询各个宏的详细描述 使用命令man 3 termios查看相关信息 配置的灵活性 通过组合不同的宏可以实现对输入模式的精细控制 配置方式的通用性 c_oflag、c_cflag和c_lflag成员也采用类似的宏配置方式 输出模式c_oflag 输出模式的定义 控制输出字符的处理方式 输出模式的作用范围 处理应用程序发送的字符数据 在数据传递到串口或屏幕之前进行处理 配置方法 通过设置struct termios结构体中的c_oflag成员 使用预定义的宏来设置标志 控制模式c_cflag 控制终端设备的硬件特性 对串口设置尤为重要 可配置的硬件特性 波特率 数据位 校验位 停止位等 配置方法 通过设置struct termios结构体中的c_cflag成员 使用预定义的标志来配置 波特率设置 Linux系统使用CBAUD位掩码来指定波特率 其他系统可能使用c_ispeed和c_ospeed成员变量 波特率操作函数 cfgetispeed()用于获取波特率 cfsetispeed()用于设置波特率 不同系统可能有不同的波特率设置方法 本地模式c_lflag 用于控制终端的本地数据处理和工作模式 通过设置 struct termios 结构体中 c_lflag 成员的标志对本地模式进行配置 特殊控制字符c_cc 特殊控制字符的定义 特定的字符组合如CtrlC、CtrlZ等 触发终端的特殊处理 实现机制 通过struct termios结构体中的c_cc数组实现 将特殊字符映射到对应的支持函数 主要特殊控制字符及其功能 a) VEOF (CtrlD): 文件结尾符 使终端驱动程序将输入行中的全部字符传递给 正在读取输入的应用程序 如果文件结尾符是该行的第一个字符则用户 程序中的 read 返回 0表示文件结束 b) VEOL (Carriage return-CR): 附加行结尾符 作用类似于行结束符 c) VEOL2 (LF): 第二行结尾符 d) VERASE (Backspace-BS): 删除操作符 使终端驱动程序删除输入行中的最后一个字符 e) VINTR (CtrlC): 中断控制字符 使终端驱动程序向与终端相连的进程发送 SIGINT 信号 f) VKILL (CtrlU): 删除行符 g) VMIN: 非规范模式下最少读取字符数 h) VQUIT (CtrlZ): 退出操作符 使终端驱动程序向与终端相连的进程发送 SIGQUIT 信号 i) VSTART (CtrlQ): 开始字符 重新启动被 STOP 暂停的输出 j) VSTOP (CtrlS): 停止字符 字符作用“截流”即阻止向终端的进一步输出 用于支持 XON/XOFF 流控 k) VSUSP (CtrlZ): 挂起字符 使终端驱动程序向与终端相连的进程发SIGSUSP 信号用于挂起当前应用程序 l) VTIME: 非规范模式下字符间超时时间 定读取的每个字符之间的超时时间以 分秒为单位TIME 特殊说明 VMIN和VTIME仅用于非规范模式 用于控制非规范模式下read()调用的行为 应用场景 这些特殊字符用于控制终端行为和进程通信 支持各种终端操作和信号发送 小结 主要内容 struct termios结构体的四个主要成员 c_iflag输入模式 c_oflag输出模式 c_cflag控制模式 c_lflag本地控制 这些参数控制和影响终端的行为特性 成员变量赋值方法 建议不要直接初始化 struct termios ter;
ter.c_iflag IGNBRK | BRKINT | PARMRK; - 推荐使用按位与、按位或等操作添加或清除标志- ter.c_iflag | (IGNBRK | BRKINT | PARMRK | ISTRIP);- 标志的适用性- 并非所有标志对所有终端设备都有效- 不同终端设备的硬件特性存在差异- 串口可以配置波特率、数据位、停止位等这些硬件参数但是其它终端是不一定支持这些配置的譬如本地终端键盘、显示器这些设备它是没有这些硬件概念的- API的通用性和局限性- 所有终端设备使用同一套API进行编程- 由于硬件差异某些配置参数可能对特定设备无效- 根据具体设备和需求选择合适的配置终端的三种工作模式 三种工作模式 a) 规范模式canonical mode b) 非规范模式non-canonical mode c) 原始模式raw mode 规范模式特点 基于行处理输入 需要输入行结束符回车符、EOF 等才能读取 支持行编辑 一次read()最多读取一行 如果在 read()函数中被请求读取的数据字节数小于当前行可读取的字节数则 read()函数只会读 取被请求的字节数剩下的字节下次再被读取 非规范模式特点 输入即时有效 不需要行结束符 不支持行编辑 通过MIN和TIME参数控制read()行为 MIN和TIME参数组合 MIN0, TIME0立即返回 若有可读数据则读取数据并返回被读取的字节数否则读取不到任何数据并返回 0 MIN0, TIME0阻塞直到满足MIN个字符 到有 MIN 个字符可以读取时才返回返回值是读取的字符数量。到达文件尾时返回 0 MIN0, TIME0有数据或超时立即返回 只要有数据可读或者经过 TIME 个十分之一秒的时间read() 函数则立即返回返回值为被读取的字节数 如果超时并且未读到数据则 read()函数返回 0 MIN0, TIME0满足MIN个字符或字符间超时才返回 在输入第一个字符后系统才会启动定时器所 以在这种情况下read()函数至少读取一个字节后才返回 原始模式 特殊的非规范模式 以字节为单位处理输入 禁用终端特殊处理 通过cfmakeraw()函数设置 cfmakeraw()函数内部其实就是对 struct termios 结构体进行了如下配置 ermios_p-c_iflag ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); termios_p-c_oflag ~OPOST; termios_p-c_lflag ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); termios_p-c_cflag ~(CSIZE | PARENB); termios_p-c_cflag | CS8; 原始模式应用场景 串口作为数据传输接口时 与其他设备或传感器通信 需要直接处理原始数据不进行ASCII转换 终端模式设置 通过struct termios结构体的c_lflag成员设置ICANON标志 默认为规范模式
打开串口设备 串口应用程序编写的第一步打开串口设备使用 open()函数打开串口的设备节点文件得到文件描述符 int fd;
fd open(“/dev/ttymxc2”, O_RDWR |O_NOCTTY); if (0 fd) { perror(“open error”); return -1; } 打开串口设备的方法 使用open()函数 打开串口的设备节点文件 获取文件描述符
获取终端当前的配置参数tcgetattr()函数 获取终端当前配置参数的目的 便于之后恢复终端到原始状态 为安全和调试考虑 使用tcgetattr()函数获取配置参数 #include termios.h #include unistd.h
int tcgetattr(int fd, struct termios *termios_p);
- 第一个参数串口终端设备的文件描述符fd- 第二个参数struct termios结构体指针用于存储配置参数- 函数返回值成功返回0。失败返回-1并设置errno 函数调用前的准备 定义struct termios结构体变量 将结构体变量的指针作为第二个参数传入 使用示例 struct termios old_cfg;
if (0 tcgetattr(fd, old_cfg)) { /* 出错处理 / do_something(); } 对串口终端进行配置 1)配置串口终端为原始模式 调用termios.h头文件中申明的 cfmakeraw()函数,这个函数没有返回值 struct termios new_cfg;
//内存清零 初始化结构体 memset(new_cfg, 0x0, sizeof(struct termios)); //配置为原始模式 cfmakeraw(new_cfg); 2)接收使能 在c_cflag成员中添加CREAD标志 new_cfg.c_cflag | CREAD; //接收使能 3)设置串口的波特率 用户不能直接通过位掩码来操作 使用cfsetispeed()和cfsetospeed()函数 cfsetispeed(new_cfg, B115200); cfsetospeed(new_cfg, B115200); B115200 是一个宏 一般来说用户需将终端的输入和输出波特率设置成一样 或使用cfsetspeed()函数同时设置输入输出波特率 cfsetspeed(new_cfg, B115200); 这几个函数在成功时返回 0失败时返回-1。 4)设置数据位大小 清除CSIZE位掩码 设置CS8为8位数据位 new_cfg.c_cflag ~CSIZE; new_cfg.c_cflag | CS8; //设置为 8 位数据位 5)设置奇偶校验位 奇校验设置PARODD和PARENB标志 偶校验设置PARENB标志清除PARODD标志 无校验清除PARENB标志 //奇校验使能 new_cfg.c_cflag | (PARODD | PARENB); new_cfg.c_iflag | INPCK;
//偶校验使能 new_cfg.c_cflag | PARENB; new_cfg.c_cflag ~PARODD; / 清除 PARODD 标志配置为偶校验 */ new_cfg.c_iflag | INPCK; //无校验 new_cfg.c_cflag ~PARENB; new_cfg.c_iflag ~INPCK; 6)设置停止位 一个停止位清除CSTOPB标志 两个停止位添加CSTOPB标志 / 将停止位设置为一个比特 new_cfg.c_cflag ~CSTOPB;
// 将停止位设置为 2 个比特 new_cfg.c_cflag | CSTOPB; 7)设置 MIN 和 TIME 的值 影响非规范模式下read()调用的行为 设置为0使read()立即返回实现非阻塞读取 new_cfg.c_cc[VTIME] 0; new_cfg.c_cc[VMIN] 0;
缓冲区的处理 缓冲区处理的必要性 使用串口前需处理缓冲区中可能存在的数据 相关函数 #include termios.h #include unistd.h
int tcdrain(int fd); int tcflush(int fd, int queue_selector); int tcflow(int fd, int action); - tcdrain() 函数- 调用后阻塞应用程序直到输出缓冲区数据全部发送完毕- tcflow() 函数- 用于暂停或重启数据传输/接收- 参数 action 决定具体操作TCOOFF, TCOON, TCIOFF, TCION- TCOOFF暂停数据输出输出传输- TCOON重新启动暂停的输出- TCIOFF发送 STOP 字符停止终端设备向系统发送数据- TCION发送一个 START 字符启动终端设备向系统发送数据- tcflush() 函数- 清空输入/输出缓冲区- 参数 queue_selector 决定清空哪些缓冲区TCIFLUSH, TCOFLUSH, TCIOFLUSH- TCIFLUSH对接收到而未被读取的数据进行清空处理- TCOFLUSH对尚未传输成功的输出数据进行清空处理- TCIOFLUSH包括前两种功能即对尚未处理的输入/输出数据进行清空处理- 以上这三个函数调用成功时返回 0失败将返回-1、并且会设置 errno 常用处理方式 使用 tcdrain() 阻塞等待数据发送完毕 tcdrain(fd); 使用 tcflush() 清空缓冲区 tcflush(fd, TCIOFLUSH);
写入配置、使配置生效tcsetattr()函数 完成struct termios结构体配置后需将参数写入终端设备使其生效 tcsetattr()函数 用于将配置参数写入硬件设备 #include termios.h #include unistd.h
int tcsetattr(int fd, int optional_actions, const struct termios termios_p); - fd文件描述符- optional_actions指定配置生效时机- TCSANOW配置立即生效- TCSADRAIN配置在所有写入 fd 的输出都传输完毕之后生效- TCSAFLUSH所有已接收但未读取的输入都将在配置生效之前被丢弃- termios_p指向struct termios对象的指针- 调用成功时返回 0失败将返回-1并设置 errno调用 tcsetattr()将配置参数写入设备使其立即生效 tcsetattr(fd, TCSANOW, new_cfg);
写数据read()、write() 所有准备工作完成之后接着便可以读写数据了直接调用 read()、write()函数即可 串口应用编程实战 #define _GNU_SOURCE //在源文件开头定义_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.htypedef 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; //串口终端对应的文件描述符/**** 串口初始化操作** 参数device表示串口终端的设备节点/ static int uart_init(const char device) {/ 打开串口终端 /fd open(device, O_RDWR | O_NOCTTY);if (0 fd) {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; }/* –dev/dev/ttymxc2 –brate115200 –dbit8 –parityN –sbit1 –typeread / /* 打印帮助信息/ static void show_help(const char *app) {printf(Usage: %s [选项]\n\n必选选项:\n –devDEVICE 指定串口终端设备名称, 譬如–dev/dev/ttymxc2\n –typeTYPE 指定操作类型, 读串口还是写串口, 譬如–typeread(read表示读、write表示写、其它值无效)\n\n可选选项:\n –brateSPEED 指定串口波特率, 譬如–brate115200\n –dbitSIZE 指定串口数据位个数, 譬如–dbit8(可取值为: 5/6/7/8)\n –parityPARITY 指定串口奇偶校验方式, 譬如–parityN(N表示无校验、O表示奇校验、E表示偶校验)\n –sbitSIZE 指定串口停止位个数, 譬如–sbit1(可取值为: 1⁄2)\n –help 查看本程序使用帮助信息\n\n, app); }/** 信号处理函数当串口有数据可读时会跳转到该函数执行/ static void io_handler(int sig, siginfo_t *info, void context) {unsigned char buf[10] {0}; // 数据缓冲区int ret;// 读取数据大小int n; // 循环变量if(SIGRTMIN ! sig)return;/ 判断串口是否有数据可读 */if (POLL_IN info-si_code) {ret read(fd, buf, 8); //一次最多读8个字节数据printf([ );for (n 0; n ret; n)printf(0x%hhx , buf[n]);printf(]\n);} }/** 异步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); }int main(int argc, char *argv[]) {uart_cfg_t cfg {0}; // 串口配置结构体初始化为0char device NULL; // 串口设备名称int rw_flag -1; // 读写标志unsigned char w_buf[10] {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88}; //通过串口发送出去的数据int n;/ 解析出参数 /for (n 1; n argc; n) {if (!strncmp(–dev, argv[n], 6))device argv[n][6];else if (!strncmp(–brate, argv[n], 8))cfg.baudrate atoi(argv[n][8]);else if (!strncmp(–dbit, argv[n], 7))cfg.dbit atoi(argv[n][7]);else if (!strncmp(–parity, argv[n], 9))cfg.parity argv[n][9];else if (!strncmp(–sbit, argv[n], 7))cfg.sbit atoi(argv[n][7]);else if (!strncmp(–type, argv[n], 7)) {if (!strcmp(read, argv[n][7]))rw_flag 0; //读else if (!strcmp(write, argv[n][7]))rw_flag 1; //写}else if (!strcmp(–help, argv[n])) {show_help(argv[0]); //打印帮助信息exit(EXIT_SUCCESS);}}if (NULL device || -1 rw_flag) {fprintf(stderr, Error: the device and read|write type must be set!\n);show_help(argv[0]);exit(EXIT_FAILURE);}/ 串口初始化 /if (uart_init(device))exit(EXIT_FAILURE);/ 串口配置 /if (uart_cfg(cfg)) {tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}/ 读|写串口 /switch (rw_flag) {case 0: //读串口数据async_io_init(); //我们使用异步I/O方式读取串口的数据调用该函数去初始化串口的异步I/Ofor ( ; ; )sleep(1); //进入休眠、等待有数据可读有数据可读之后就会跳转到io_handler()函数break;case 1: //向串口写入数据for ( ; ; ) { //循环向串口写入数据write(fd, w_buf, 8); //一次向串口写入8个字节sleep(1); //间隔1秒钟}break;}/ 退出 */tcsetattr(fd, TCSANOW, old_cfg); //恢复到之前的配置close(fd);exit(EXIT_SUCCESS); } 用户参数解析 串口设备节点、波特率、数据位、停止位、奇偶校验和读写模式 如果用户输入了–help参数程序会调用show_help()函数打印使用帮助信息并退出程序
串口初始化 调用uart_init()函数 打开指定的串口终端设备 获取当前的串口配置参数保存到old_cfg中
串口配置 调用uart_cfg()函数 设置为原始模式 使能接收功能 根据输入的波特率、数据位、停止位和奇偶校验进行相应的设置 清空串口缓冲区并将新配置应用到串口设备
操作模式选择 根据输入的–type参数决定进行读取还是写入操作 如果是read程序将进入异步读取模式 如果是write程序将循环写入数据到串口
异步I/O初始化仅用于读取 调用async_io_init()为异步I/O设置信号处理 使能异步I/O 设置异步I/O的所有者 定实时信号SIGRTMIN作为异步I/O通知信号 注册io_handler()函数当有数据可读时会被调用 程序会读取串口中的数据并打印。一次最多读取8个字节超出的数据将在后续的读取中处理
写入操作仅用于写入模式 在写入模式下程序循环向串口写入固定的数据w_buf数组内容并每隔1秒进行一次写入 退出 在程序结束前恢复串口到原来的配置old_cfg关闭串口设备并正常退出
- 上一篇: 珠海高端网站建设报价超级简历网站
- 下一篇: 珠海公司网站设计wordpress汉语插件
相关文章
-
珠海高端网站建设报价超级简历网站
珠海高端网站建设报价超级简历网站
- 技术栈
- 2026年03月21日
-
珠海服务好的网站建设网站建设要托管服务器
珠海服务好的网站建设网站建设要托管服务器
- 技术栈
- 2026年03月21日
-
珠海定制网站建设推广常州辉煌网络网站建设
珠海定制网站建设推广常州辉煌网络网站建设
- 技术栈
- 2026年03月21日
-
珠海公司网站设计wordpress汉语插件
珠海公司网站设计wordpress汉语插件
- 技术栈
- 2026年03月21日
-
珠海公司制作网站传奇游戏在线玩
珠海公司制作网站传奇游戏在线玩
- 技术栈
- 2026年03月21日
-
珠海公司制作网站陕西住房建设部网站
珠海公司制作网站陕西住房建设部网站
- 技术栈
- 2026年03月21日
