Linux串口编程短信篇(一) ——— 串口通信初始化

文章目录

  • 一. 前言
  • 二. 短信篇介绍
  • 三. 串口初始化
    • 3.1 流程图
    • 3.2 代码

一. 前言

 前面关于串口通信,串口编程的文章也总结了许多,之前的有一篇文章,写的是编程实现busybox microcom 工具,将功能模块一一分开,其实有一点多此一举了,但是作为我学习Linux下串口编程的记录,我还是决定不修改这篇博客,但是,既然作为串口编程的c文件,应该将所有用于串口通信的功能模块归纳进一个c文件 comport.c ,头文件均放置在 comport.h 中,然后写一个main函数来进行调用,这样,才是一个更具有整体性和完整性的调试工具,后续也修改了不能发送短信的bug,如果需要源码的可以私聊.

二. 短信篇介绍

作为短信篇的开篇,有必要说明一下我想实现的是什么,需要掌握哪些知识…

无论是busybox 中的microcom还是我自己写出来的comport工具,都有一个头大问题,那就是,发送中文短信时,需要进行PDU的编码,我还记得刚拿到4g模块时,就遇到了很多问题,在ATD终于拨通我的手机那一瞬间,眼泪掉下来,紧接着又百度了AT发送短信的命令,看到发送短信还分为TEXT和PDU简直一脸懵逼,在尝试发送TEXT格式短信成功后,兴奋又准备去尝试PDU格式的短信,可是,当我的鼠标下移后,惊恐的发现,这是一个New World…

终于搞了半天才弄明白怎么发送PDU格式的短信,可是,这仅仅停留在别人全部把现成的编码方式告诉我,而且在UTF-8Unicode 时,用的还是网页在线转码(有点丢人),在这么一个较为繁琐的编码和计算之下,难免会遇到很多错误,下面是我遇到的一些错误:

  1. 再输入PDU编码前一步是输入AT+CMGS=len,len是PDU编码时的一个中间产物,短信发送时,只有输入的PDU编码严格满足这个值,短信才可以发送.
  2. 网上找了很多方法来实现 UTF-8Unicode ,有使用wchar_t类型借助wcseln函数实现的,有使用iconv函数实现的,但是最终我还是选择搞懂他们之间的转换规则,自己写代码来实现
  3. 编码时可能会漏东西,检测出错误还好,如果恰巧AT+CMGS的值是正确的,那接收到的短信就会是一些奇奇怪怪的文字
  4. 换卡以后,短信中心号码也变了,又要重新按照步骤处理短信中心号
  5. 电话号码也是需要处理的

总结,每发一次短信,都需要折腾很久,而且能一次成功的几率甚小,于是,我就打算写一个程序,只需要输入收件人号码,短信内容就可以发送短信了,就像手机一样,第一个版本需要手动输入短信中心号,不过后来优化了,下面就来看看我是怎么实现的吧~

三. 串口初始化

关于串口的初始化,我在下面这篇博客做了非常详细的讲解,这里,就总体的梳理梳理
传送门

3.1 流程图

打开串口
在这里插入图片描述
因为函数都是自己编写,所以可以统一返回值,我的函数如果不是特殊要求(后面的文章会提到),成功返回0,失败返回负数。

串口初始化
在这里插入图片描述
其他的就不过多赘述了,这些都是为发送短信而铺的基石,文章末尾的博客详细总结了这方面内容。

3.2 代码

串口的初始化,服务于后面的程序,只有先将串口的准备工作做完,才可以进行后面的工作

comport.h

/*********************************************************************************      Copyright:  (C) 2020 LuXiaoyang<920916829@qq.com>*                  All rights reserved.**       Filename:  comport.h*    Description:  This head file of comport.c**        Version:  2.0.0(11/07/20)*         Author:  LuXiaoyang <920916829@qq.com>*      ChangeLog:  1, Release initial version on "11/07/20 16:09:33"*                 ********************************************************************************/
#ifndef  _COMPORT_H_
#define  _COMPORT_H_#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>#define SERIALNAME_LEN 64typedef struct _st_ComportAttr {int               fd;        //串口文件描述符int               BaudRate;  //波特率int               DataBits;  //数据位char              Parity;    //奇偶校验位int               StopBits;  //停止位int               mSend_Len; //单次最大发送长度char              SerialName[SERIALNAME_LEN];  //串口名称struct termios    OldTermios;  //串口的原始属性
}ComportAttr;/* Use the serial port name(comport->SerialName) to open and assign the returned file descriptor to comport->fd */
int comport_open(ComportAttr *comport);/* Set the serial port's baud rate, data bits, parity and other attributes, and set the related Peugeot bits for serial communication */
int comport_init(ComportAttr *comport);/* Send the serial port command in the parameter and can handle the command with too long length */
int comport_send(ComportAttr *comport,char *sbuf,int sbuf_len);/* Receive serial data, you can use select multiplexing to specify the time of read according to the value of timeout */
int comport_recv(ComportAttr *comport,char *rbuf,int rbuf_len,int timeout);/* Close the serial port, clear the serial port buffer, and release the memory */
int comport_close(ComportAttr *comport);#endif   /* ----- #ifndef _COMPORT_H_  ----- */


comport.c

/**********************************************************************************      Copyright:  (C) 2020 LuXiaoyang<920916829@qq.com>*                  All rights reserved.**       Filename:  comport.c*    Description:  The file contains the functions of opening, closing, *                  initializing, and communicating with the serial port.*                 *        Version:  1.0.0(11/07/20)*         Author:  LuXiaoyang <920916829@qq.com>*      ChangeLog:  1, Release initial version on "11/07/20 15:07:25"*                 ********************************************************************************/
#include "comport.h"int comport_open(ComportAttr *comport)
{int                retval = -1;if(NULL == comport){printf("%s,Invalid parameter\n",__func__);return retval;}/* * O_NOCTTY表示打开的是一个终端设备,程序不会成为该* 端口的控制终端,O_NONBLOCK使得read处于非阻塞模式 ** */comport->fd = open(comport->SerialName,O_RDWR | O_NOCTTY | O_NONBLOCK);if(comport->fd < 0){printf("%s,Open %s failed:%s\n",__func__,comport->SerialName,strerror(errno));return -1;}/* 检查串口是否处于阻塞态 */if((retval = fcntl(comport->fd,F_SETFL,0)) < 0){printf("%s,Fcntl check faile.\n",__func__);return -2;}printf("Starting serial communication process ");/* 检查该文件描述符是否对应了终端设备 */if(0 == isatty(comport->fd)){printf("%s:[%d] is not a Terminal equipment.\n",comport->SerialName,comport->fd);return -3;}printf("Open %s successfully.\n",comport->SerialName);return 0;
}int comport_close(ComportAttr *comport)
{if(tcflush(comport->fd,TCIOFLUSH))  //清零用于串口通信的缓冲区{printf("%s,Tcflush faile:%s\n",__func__,strerror(errno));return -1;}/* 将串口设置为原有属性 */if(tcsetattr(comport->fd,TCSANOW,&(comport->OldTermios))){printf("%s,Set old options failed:%s\n",__func__,strerror(errno));return -2;}close(comport->fd);free(comport);return 0;
}/* 初始化串口属性,设置串口用于通信 */
int comport_init(ComportAttr *comport)
{char                  baudrate[32] = {0};struct termios        NewTermios;memset(&NewTermios,0,sizeof(struct termios));memset(&(comport->OldTermios),0,sizeof(struct termios));if(!comport){printf("Invalid parameter.\n");return -1;}/* 获取串口原始属性,这部分是备份 */if(tcgetattr(comport->fd,&(comport->OldTermios))){printf("%s,Get termios to OldTermios failure:%s\n",__func__,strerror(errno));return -2;}/* 获取串口原始属性,这部分用于设置新属性 */if(tcgetattr(comport->fd,&NewTermios)){printf("%s,Get termios to NewTermios failure:%s\n",__func__,strerror(errno));return -3;}/* 修改控制模式,保证程序不会占用串口 */NewTermios.c_cflag |= CLOCAL;/*  For example:*   *      c_cflag:   0 0 0 0 1 0 0 0*      CLOCAL:  | 0 0 0 1 0 0 0 0*              --------------------*                 0 0 0 1 1 0 0 0*                *  Finally:**     c_flag = 0 0 0 1 1 0 0 0;** *//* 启动接收器,能够从串口中读取输入数据 */NewTermios.c_cflag |= CREAD;/*  CSIZE字符大小掩码,将与设置databits相关的标致位置零 */NewTermios.c_cflag &= ~CSIZE;/* For example:**      CSIZE = 0 1 1 1 0 0 0 0 ---> ~CSIZE = 1 0 0 0 1 1 1 1**      c_cflag:    0 0 1 0 1 1 0 0*      ~CSIZE:  &  1 0 0 0 1 1 1 1     *              -----------------------*                  0 0 0 0 1 1 0 0** Finally:**     c_cflag = 0 0 0 0 1 1 0 0** */NewTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* * ICANON: 标准模式* ECHO:   回显所输入的字符* ECHOE:  如果同时设置了ICANON标志,ERASE字符删除前一个所输入的字符,WERASE删除前一个输入的单词* ISIG:   当接收到INTR/QUIT/SUSP/DSUSP字符,生成一个相应的信号** */NewTermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* * BRKINT: BREAK将会丢弃输入和输出队列中的数据(flush),并且如果终端为前台进程组的控制终端,则BREAK将会产生一个SIGINT信号发送到这个前台进程组* ICRNL:  将输入中的CR转换为NL* INPCK:  允许奇偶校验* ISTRIP: 剥离第8个bits* IXON:   允许输出端的XON/XOF流控** *//* OPOST: 表示处理后输出,按照原始数据输出 */NewTermios.c_oflag &= ~(OPOST);if(comport->BaudRate){sprintf(baudrate,"B%d",comport->BaudRate);cfsetispeed(&NewTermios,(int)baudrate); //设置输入输出波特率cfsetospeed(&NewTermios,(int)baudrate);}else{cfsetispeed(&NewTermios,B115200);cfsetospeed(&NewTermios,B115200);}/* 设置数据位 */switch(comport->DataBits){case '5':NewTermios.c_cflag |= CS5;break;case '6':NewTermios.c_cflag |= CS6;break;case '7':NewTermios.c_cflag |= CS7;break;case '8':NewTermios.c_cflag |= CS8;break;default:NewTermios.c_cflag |= CS8;  //默认数据位为8break;}/* 设置校验方式 */switch(comport->Parity){/* 无校验 */case 'n':case 'N':NewTermios.c_cflag &= ~PARENB;NewTermios.c_iflag &= ~INPCK;break;/* 偶校验 */case 'e':case 'E':NewTermios.c_cflag |= PARENB;NewTermios.c_cflag &= ~PARODD;NewTermios.c_iflag |= INPCK;break;/* 奇校验 */case 'o':case 'O':NewTermios.c_cflag |= PARENB;NewTermios.c_cflag |= PARODD;NewTermios.c_iflag |= INPCK;/* 设置为空格 */case 's':case 'S':NewTermios.c_cflag &= ~PARENB;NewTermios.c_cflag &= ~CSTOPB;/* 默认无校验 */default:NewTermios.c_cflag &= ~PARENB;NewTermios.c_iflag &= ~INPCK;break;}/* 设置停止位 */switch(comport->StopBits){case '1':NewTermios.c_cflag &= ~CSTOPB;break;case '2':NewTermios.c_cflag |= CSTOPB;break;default:NewTermios.c_cflag &= ~CSTOPB;  //默认使用1位作为停止位break;}NewTermios.c_cc[VTIME] = 0;  //最长等待时间NewTermios.c_cc[VMIN] = 2;  //最小接收字符 comport->mSend_Len = 128;  //若命令长度大于mSend_Len,则每次最多发送为mSend_Len/*  清空用于串口通信的输入输出缓存区 */if(tcflush(comport->fd,TCIFLUSH)){printf("%s,Failed to clear the cache:%s\n",__func__,strerror(errno));return -4;}/* 设置串口属性,除了查看波特率的函数,其余用于串口通信的函数成功均返回0 */if(tcsetattr(comport->fd,TCSANOW,&NewTermios) != 0){printf("%s,tcsetattr failure:%s\n",__func__,strerror(errno));return -5;}printf("Comport Init Successfully......\n");return 0;}/* 向串口发送相关指令 */
int comport_send(ComportAttr *comport,char *sbuf,int sbuf_len)
{char     *ptr,*end;int       retval;if(!comport || !sbuf || sbuf_len <= 0){printf("%s,Invalid parameter.\n",__func__);return -1;}if(sbuf_len > comport->mSend_Len)  //指令长度实际长度大于单次发送的最大长度,则每次发送单次发送的最大长度{ptr = sbuf;end = sbuf + sbuf_len;do{if(comport->mSend_Len < (end - ptr))  //剩余长度大于单次发送的最大长度{retval = write(comport->fd,ptr,comport->mSend_Len);if(retval <= 0 || retval != comport->mSend_Len){printf("Write to com port[%d] failed:%s\n",comport->fd,strerror(errno));return -2;}ptr += comport->mSend_Len;}else{retval = write(comport->fd,ptr,(end - ptr));  //剩余长度可一次性发送if(retval <= 0 || retval != (end - ptr)){printf("Write to com port[%d] failed:%s\n",comport->fd,strerror(errno));return -3;}ptr += (end - ptr);}}while(end > ptr);}else{retval = write(comport->fd,sbuf,sbuf_len);if(retval <= 0 || retval != sbuf_len){printf("Write to com port[[%d] failed:%s\n",comport->fd,strerror(errno));return -4;}}return retval;
}int comport_recv(ComportAttr *comport,char *rbuf,int rbuf_len,int timeout)
{int                   retval;fd_set                rset;struct timeval        time_out;if(!rbuf || rbuf_len <= 0){printf("%s,Invalid parameter.\n",__func__);return -1;}if(timeout) //如果传入该参数,则调用select指定读的阻塞时间{time_out.tv_sec = (time_t)timeout;time_out.tv_usec = 0;FD_ZERO(&rset);FD_SET(comport->fd,&rset);retval = select(comport->fd + 1,&rset,NULL,NULL,&time_out);if(retval < 0){printf("%s,Select failed:%s\n",__func__,strerror(errno));return -2;}else if(0 == retval){printf("Time Out.\n");return 0;}}/* 延时,避免数据还未到 */usleep(1000);retval = read(comport->fd,rbuf,rbuf_len);if( retval <= 0){printf("%s,Read failed:%s\n",__func__,strerror(errno));return -3;}return retval;}

具体标志位的设置,函数的使用,comport_send(),comport_recv() 的实现思路,请参考下方博客,要想完成这个程序,搞懂初始化的步骤很有必要:

Linux串口编程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/256178.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux串口编程短信篇(三) ——— AT指令封装实现Check系列函数及SMS函数封装

文章目录 一. 很重要的函数: send_at_cmd()1.1 设计思路1.2 send_at_cmd() 二. Check系列函数2.1 check_comport_ready()2.2 check_if_there_is_sim()2.3 check_sim_login()2.4 check_sim_signal()2.5 check_sim_allready() 三. 获取短信中心号码四. TEXT / PDU SMS SEND4.1 TE…

Compareable接口

重写 compareTo方法如下&#xff1a; public int compareTo(Object o) {Name n (Name) o;int lastCmp secondName.compareTo(n.secondName);return(lastCmp!0 ? lastCmp:firstName.compareTo(n.firstName));}完整代码如下 import java.util.List; import java.util.LinkedLi…

@ServeletComponentScan和@ComponentScan的区别

一、SpringBoot中使用Servlet 在SpringBootApplication上使用ServletComponentScan注解后&#xff0c;Servlet、Filter、Listener可以直接通过WebServlet、WebFilter、WebListener注解自动注册&#xff0c;无需其他代码。 1.在入口Application类上加入注解ServletComponentSca…

Linux系统imx6ull开发板内核安装STM Virtual ComPort虚拟串口驱动

目录 前言 一、问题描述 二、解决步骤 1.更改option.c文件 2.修改内核编译选项 3.编译内核并加载到开发板 总结 前言 本文硬件使用正点原子imx6ull-min底板emmc ddr512核心板&#xff0c;软件使用正点原子针对imx6ull开发板提供的出场Linux内核源码、设备树与根文件系统&#…

MDK5:正点原子stm32遇Target not created的解决方法

几乎在直接打开这里的test.uvprojx文件进行编译运行时都会出现以上的错误。这时候只需关闭改工程&#xff0c;然后右击keil5的应用程序&#xff0c;点击“以管理员身份运行”&#xff0c;打开之后即可编译成功。

MDK5 添加Device

现象如下图&#xff1a; 出现这个问题的原因有两种&#xff0c;因此有两种解决方法&#xff1a; 第一种&#xff1a;大部分为网上介绍的&#xff0c;没有下载Pack包&#xff0c;进入Packs Installer中选择自己需要的芯片系列选择包进行下载即可。 第二种&#xff1a;不常见的…

Keil MDK 软件包(MDK5 Software Packs)离线下载方法

最近 Keil 官网的 MDK 软件包下载页面不展示软件包的下载列表了&#xff0c;无法下载软件包&#xff0c;如图1所示。 图1 Keil 官网 MDK5 Software Packs 下载页面截图 其实是有办法下载软件包的&#xff0c;方法如下&#xff1a; 1、打开 Microsoft Edge 浏览器&#xff08;其…

Keil MDK5 打开MDK4项目

安装完最新版本keil(5.38a)后&#xff0c;需要打开几个MDK4的项目&#xff0c;结果一打开keil就提示报错了。 这里我选择的是第二种方式&#xff0c;首先安装legacy support&#xff0c;以下是下载链接 MDK v4 Legacy Support (keil.com) 安装完成以后&#xff0c;可能会遇到另…

keil MDK5 无法 Go To Definition Of

目录 1.勾选 Browse Information 2.重新编辑即可 弹出如下窗口&#xff1a; 编译没有通过&#xff0c;或者函数有编译条件限制&#xff08;在某个条件成立时&#xff0c;函数才会被keil编译&#xff09;&#xff0c;没有被编译的函数是不能使用“Go To Definition Of”跳转到…

MDK5__配色方案

这个MDK5的配色方案是仿照Notepad里面的Plastic Code Wrap主题&#xff0c;字体是Courier New&#xff0c;个人比较喜欢这个主题配色方案&#xff0c;同时经常看代码不会觉得太刺眼 配色过程 修改颜色有两种方案&#xff0c;最简单的是在MDK软件中&#xff0c;通过Edit->C…

MDK5的安装

一、安装MDK5 下载&#xff1a; 官方下载地址&#xff1a;http://www2.keil.com/mdk5 1.打开官方网站&#xff0c;并点击下载MDK5 2.按照要求填写信息并点击提交 3.点击下载 安装 1.双击图标进行安装 2.进入安装界面——点击Next&#xff08;下一步&#xff09; 3.选中…

ARM KEIL: MDK5 Software Packs

ARM&#xff1a;Advanced RISC Machines 先进的精简指令集器件。 ARM是最当下最流行的处理器&#xff0c;尤其在嵌入式领域。 KEIL被ARM收购&#xff0c;MDK5是ARM官方的开发工具。 作为一款面向微控制器和微处理器的软件开发工具&#xff0c;需要支持各种各样的芯片。 所以…

STM32——新建 Keil MDK 5 工程(寄存器版本)

注&#xff1a;本文仅依照加粗字体和图片可快速新建 Keil MDK 5 工程 由于文章没有细分步骤&#xff0c;标记点用于方便阅读。 &#x1f53a; 标记点 ① 打开 Keil MDK 5&#xff0c;点击Project > New μVision Project&#xff0c;如图&#xff1a; &#x1f53a; 标记…

MDK5之.map文件解析

MDK5之.map文件解析 前言一、.map文件总览二、详细介绍1、Section Cross References&#xff1a;模块、段(入口)交叉引用2、Removing Unused input sections from the image&#xff1a;移除未使用的模块3、Image Symbol Table&#xff1a;映射符号表4、Memory Map of the imag…

MDK5 与 GIT 配合使用

参考 Abstract: (keil.com) 1&#xff0c;增加忽略文件 (.gitignore) &#xff08;我一般喜欢把输出文件放到Objects/目录下&#xff09; Listings/ Objects/ *.TMP 2 最好是mdk5.15以上版本&#xff0c;不然需要拷贝文件到此目录 Vision 5.15 is providing a SVCS templ…

Keil MDK5硬件仿真之基本介绍

Keil MDK5这个软件是提供硬件仿真功能的&#xff0c;所谓硬件仿真就是指将当前的程序实时的运行在你的设备中&#xff0c;并且可以检测程序中的变量的值&#xff0c;或者控制程序运行的流程&#xff0c;下面就让我们来看一下如何进行硬件仿真吧。 第一步&#xff0c;单击Optio…

keil MDK5软件包介绍、下载、安装与分享

前言 本文介绍了Keil MDK5软件包的分类、作用、下载、安装与更新。软件包下载可通过Keil自带的Pack Installer、进入Keil Pack下载网站手动下载、去芯片厂家官网下载三种方式。同时分享了一个小技巧&#xff0c;可以直接分享已安装好的软件包给别人。 一. Keil MDK软件包介绍 K…

Ubuntu安装MDK5

1 环境部署 Ubuntu 18.04 Wine 3.0.4 1.1 查看CPU信息 lscpu序号属性描述1架构x86_642CPU 运行模式32-bit, 64-bit3字节序Little Endian4CPU45在线 CPU 列表0-36每个核的线程数17每个座的核数48座19NUMA 节点110厂商 IDGenuineIntel11CPU 系列613型号15814型号名称Intel Core…

下载MDK5 Software Packs导入Keil5

⭐最近在用Keil5编译STM32L系列的项目文件&#xff0c;在选择芯片型号时并没有自己的STM32L系列的芯片&#xff0c;找了很久&#xff0c;才找到下载的网址&#xff0c;现在来分享一下自己下载和安装的过程。&#x1f308; 1.下载网址 MDK Software Packs下载地址 以上就是MDK5的…

MDK5注释乱码以及解决方案

在MDK5中会出现注释乱码的情况 //GPIO_SetBits(GPIOB,GPIO_Pin_7);//0GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; //GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; /…