-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 94.3 KB
/
content.json
1
{"meta":{"title":"zhiPeng","subtitle":"","description":"","author":"zhiPeng","url":"https://linzhipeng.top","root":"/"},"pages":[{"title":"404 Not Found","date":"2021-02-11T06:54:18.447Z","updated":"2020-02-06T09:09:48.127Z","comments":true,"path":"404.html","permalink":"https://linzhipeng.top/404.html","excerpt":"","text":"404 Not Found **很抱歉,您访问的页面不存在** 可能是输入地址有误或该地址已被删除"},{"title":"","date":"2021-02-11T06:49:06.780Z","updated":"2021-02-11T06:49:06.785Z","comments":true,"path":"about/index.html","permalink":"https://linzhipeng.top/about/index.html","excerpt":"","text":"����д�����Լ�������"},{"title":"所有分类","date":"2021-02-11T07:11:35.224Z","updated":"2020-02-06T09:09:48.135Z","comments":true,"path":"categories/index.html","permalink":"https://linzhipeng.top/categories/index.html","excerpt":"","text":""},{"title":"我的朋友们","date":"2021-02-11T07:10:51.715Z","updated":"2020-02-09T12:38:38.602Z","comments":true,"path":"friends/index.html","permalink":"https://linzhipeng.top/friends/index.html","excerpt":"","text":"刚刚建站,有兴趣添加友情链接的欢迎下方留言"},{"title":"","date":"2021-02-11T06:51:21.163Z","updated":"2021-02-11T06:51:21.167Z","comments":true,"path":"mylist/index.html","permalink":"https://linzhipeng.top/mylist/index.html","excerpt":"","text":""},{"title":"","date":"2021-02-12T08:48:41.114Z","updated":"2021-02-12T08:48:41.121Z","comments":true,"path":"photo/index.html","permalink":"https://linzhipeng.top/photo/index.html","excerpt":"","text":"相册xaoxuu简约风格 inkss这是一段关于这个网站的描述文字 MHuiG这是一段关于这个网站的描述文字 Colsrch这是一段关于这个网站的描述文字 Linhk1606这是一段关于这个网站的描述文字 视频xaoxuu简约风格 inkss这是一段关于这个网站的描述文字"},{"title":"","date":"2021-02-12T08:48:42.554Z","updated":"2021-02-12T08:48:42.561Z","comments":true,"path":"projects/index.html","permalink":"https://linzhipeng.top/projects/index.html","excerpt":"","text":"第一 xaoxuu xaoxuu 第二 下载源码 查看文档 第三 心率管家 专业版 心率管家 免费版"},{"title":"所有标签","date":"2021-02-11T07:11:11.232Z","updated":"2020-02-09T12:34:41.462Z","comments":true,"path":"tags/index.html","permalink":"https://linzhipeng.top/tags/index.html","excerpt":"","text":""},{"title":"视频","date":"2020-12-20T10:31:22.295Z","updated":"2020-12-20T10:31:22.299Z","comments":false,"path":"video/index.html","permalink":"https://linzhipeng.top/video/index.html","excerpt":"","text":"asdas 如何参与项目https://volantis.js.org/contributors/ asdsdad ghbdf xaoxuu简约风格 inkss这是一段关于这个网站的描述文字 MHuiG这是一段关于这个网站的描述文字 Colsrch这是一段关于这个网站的描述文字 Linhk1606这是一段关于这个网站的描述文字"}],"posts":[{"title":"H.264基础知识总结","slug":"音视频/H.264基础知识总结","date":"2021-04-24T18:09:17.000Z","updated":"2021-04-24T10:27:28.100Z","comments":true,"path":"2021/04/24/音视频/H.264基础知识总结/","link":"","permalink":"https://linzhipeng.top/2021/04/24/%E9%9F%B3%E8%A7%86%E9%A2%91/H.264%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/","excerpt":"H264是视频编解码格式;学习H264之前首先要搞明白一个问题,视频为什么要编码,编码传输不行吗?视频就是一堆图片按时间顺序播放,在编码标准出现之前,不经过编码的原始码流,可以这么理解成就是携带时间戳的图片。可以随便找个图片看下他的大小小则几十K,比较清晰的甚至需要十几M。这么大的数据量造成了,视频在存储时会耗费大量的存储空间,网络传输时占用大量带宽和流量,而编码就是为了减少视频的数据量,减少存储和网络中浪费的资源,在发送端做压缩在接收端做解压;接收端播放时将压缩后的数据量依据编解码算法和编解码规则,解码出来的视频和不经过压缩后传输的效果做到几乎一样;编码的本质是压缩","text":"H264是视频编解码格式;学习H264之前首先要搞明白一个问题,视频为什么要编码,编码传输不行吗?视频就是一堆图片按时间顺序播放,在编码标准出现之前,不经过编码的原始码流,可以这么理解成就是携带时间戳的图片。可以随便找个图片看下他的大小小则几十K,比较清晰的甚至需要十几M。这么大的数据量造成了,视频在存储时会耗费大量的存储空间,网络传输时占用大量带宽和流量,而编码就是为了减少视频的数据量,减少存储和网络中浪费的资源,在发送端做压缩在接收端做解压;接收端播放时将压缩后的数据量依据编解码算法和编解码规则,解码出来的视频和不经过压缩后传输的效果做到几乎一样;编码的本质是压缩 H264基础概念 序列 H264编码标准中所遵循的理论依据个人理解成:参照一段时间内相邻的图像中,像素、亮度与色温的差别很小。所以当面对一段时间内图像我们没必要去对每一幅图像进行完整一帧的编码,而是可以选取这段时间的第一帧图像作为完整编码,而下一幅图像可以记录与第一帧完整编码图像像素、亮度与色温等的差别即可,以此类推循环下去。什么叫序列呢?上述的这段时间内图像变化不大的图像集我们就可以称之为一个序列。序列可以理解为有相同特点的一段数据。但是如果某个图像与之前的图像变换很大,很难参考之前的帧来生成新的帧,那么久结束删一个序列,开始下一段序列。重复上一序列的做法,生成新的一段序列。 GOP(画面组)GOP我个人也理解为跟序列差不多意思,就是一段时间内变化不大的图像集。GOP结构一般有两个数字,如M=3,N=12。M指定I帧和P帧之间的距离,N指定两个I帧之间的距离。上面的M=3,N=12,GOP结构为:IBBPBBPBBPBBI。在一个GOP内I frame解码不依赖任何的其它帧,p frame解码则依赖前面的I frame或P frame,B frame解码依赖前最近的一个I frame或P frame 及其后最近的一个P frame。 帧类型H264结构中,一个视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成,一个宏块由4x4-16x16的yuv数据组成。宏块作为H264编码的基本单位。 在H264协议内定义了三种帧,分别是I帧、B帧与P帧。I帧就是之前所说的一个完整的图像帧,而B、帧与P帧所对应的就是之前说的不编码全部图像的帧。P帧与B帧的差别就是P帧是参考之前的I帧而生成的,而B帧是参考前后图像帧编码生成的。 IDR帧(关键帧)在编码解码中为了方便,将GOP中首个I帧要和其他I帧区别开,把第一个I帧叫IDR,这样方便控制编码和解码流程,所以IDR帧一定是I帧,但I帧不一定是IDR帧;IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始算新的序列开始编码。I帧有被跨帧参考的可能,IDR不会。 宏块宏块是H264编码的关键,H264默认是使用 16X16 (像素点)大小的区域作为一个宏块,也可以划分成 8X8 大小。 视频中相邻的两个图片之间的数据不大,两张图片中有大量的重复数据,对于这些以不同的只是宏块的位置,所以第二章图片只要记录这些相似图片的矢量位移就可以。 编码算法(帧内压缩和帧间压缩)帧内(Intraframe)压缩也称为空间压缩(Spatialcompression)。当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩本质是对宏块的压缩和预测。帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。帧间压缩也称为时间压缩(Temporalcompression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值(Framedifferencing)算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。帧间压缩本质是记录宏块的运动信息。 H264码流格式总体来看H264的结构如下图细分到每个图像序列,(GOP,也就是上面视频序列中的一个节点)它的文件结构如下我们可以看到,H264码流是由一个个的NAL单元组成,其中SPS、PPS、IDR和SLICE是NAL单元某一类型的数据;实际的网络数据传输过程中H264的数据结构是以NALU(NAL单元)进行传输的,传输数据结构组成为[NALU Header]+[RBSP]一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU 单元的开始,必须是”00 00 00 01” 或”00 00 01”NAL单元的头部是由forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)三个部分组成的,组成如图6所示:1、F(forbiden):禁止位,占用NAL头的第一个位,当禁止位值为1时表示语法错误;2、NRI:参考级别,占用NAL头的第二到第三个位;值越大,该NAL越重要。3、Type:Nal单元数据类型,也就是标识该NAL单元的数据类型是哪种,占用NAL头的第四到第8个位; 其中nal_unit_type是比较重要的,所以记录一下 每种类型都有代表一种数据类型,比较重要的以下几种做个简单的介绍:1、非VCL的NAL数据类型:1)、SPS(序列参数集):SPS对如标识符、帧数以及参考帧数目、解码图像尺寸和帧场模式等 解码参数进行标识记录。2)、PPS(图像参数集):PPS对如熵编码类型、有效参考图像的数目和初始化等解码参数进行标志记录。3)、SEI(补充增强信息):这部分参数可作为H264的比特流数据而被传输,每一个SEI信息被封装成一个NAL单元。SEI对于解码器来说可能是有用的,但是对于基本的解码过程来说,并不是必须的。 @:先标记一下,SPS、PPS内容是编码器给的。(出处的话,慢慢研究) 2、VCL的NAL数据类型1)、 头信息块,包括宏块类型,量化参数,运动矢量。这些信息是最重要的,因为离开他们,被的数据块种的码元都无法使用。该数据分块称为A类数据分块。2)、 帧内编码信息数据块,称为B类数据分块。它包含帧内编码宏块类型,帧内编码系数。对应的slice来说,B类数据分块的可用性依赖于A类数据分块。和帧间编码信息数据块不通的是,帧内编码信息能防止进一步的偏差,因此比帧间编码信息更重要。3)、 帧间编码信息数据块,称为C类数据分块。它包含帧间编码宏块类型,帧间编码系数。它通常是slice种最大的一部分。帧间编码信息数据块是不重要的一部分。它所包含的信息并不提供编解码器之间的同步。C类数据分块的可用性也依赖于A类数据分块,但于B类数据分块无关。以上三种数据块每种分割被单独的存放在一个NAL单元中,因此可以被单独传输。 参考资料:https://blog.csdn.net/guoyunfei123/article/details/106202362https://www.bilibili.com/video/BV137411C7hS","categories":[{"name":"-音视频","slug":"音视频","permalink":"https://linzhipeng.top/categories/%E9%9F%B3%E8%A7%86%E9%A2%91/"}],"tags":[{"name":"-音视频 -h.264","slug":"音视频-h-264","permalink":"https://linzhipeng.top/tags/%E9%9F%B3%E8%A7%86%E9%A2%91-h-264/"}],"author":"lzp"},{"title":"MQTT——快速搭建客户端和服务器","slug":"embedded/IOT/MQTT——快速搭建客户端和服务器","date":"2021-01-31T18:35:51.000Z","updated":"2021-02-12T12:12:17.575Z","comments":true,"path":"2021/01/31/embedded/IOT/MQTT——快速搭建客户端和服务器/","link":"","permalink":"https://linzhipeng.top/2021/01/31/embedded/IOT/MQTT%E2%80%94%E2%80%94%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%92%8C%E6%9C%8D%E5%8A%A1%E5%99%A8/","excerpt":"MQTT是物联网中应用及其广泛的应用协议,mqtt由于其带宽低和基于发布订阅的模式的优点,被各物联网平台推广使用。MQTT首先有三个角色:订阅者、发布者、中间代理。订阅者通过订阅指定消息类型可收到发布者发布的制定类型消息。服务器作为代理做消息的转发。这种基于发布订阅的模式与物联网中边缘终端1对N的特征完全匹配。不得不说MQTT协议天生为物联网而生。","text":"MQTT是物联网中应用及其广泛的应用协议,mqtt由于其带宽低和基于发布订阅的模式的优点,被各物联网平台推广使用。MQTT首先有三个角色:订阅者、发布者、中间代理。订阅者通过订阅指定消息类型可收到发布者发布的制定类型消息。服务器作为代理做消息的转发。这种基于发布订阅的模式与物联网中边缘终端1对N的特征完全匹配。不得不说MQTT协议天生为物联网而生。 本文将分两部分总结下搭建基于MQTT协议的服务端和客户端。 一、serverMQTT作为一种协议被各厂家实现了各种版本,mqtt的官网https://mqtt.org 也提供了各个源码的链接。这里使用emqx作为服务器,emqx为用户提供了后台管理界面,程序运行后可直接通过web查看或更改服务。emqx官网有介绍各种平台的不同安装方式,这里建议使用官网提供的一键安装脚本 1curl https://repos.emqx.io/install_emqx.sh | bash 安装完成后可直接通过emqx start运行服务,服务打开后可通过web访问后台的方式查看各种信息。默认端口是18083。如果是云服务器记得开放该端口访问权限。 默认是英文显示,可以自行在设置中更改语言和显示主题。同时由于emqx还提供websocket方式订阅发布消息。后台Dashboard也提供了通过websocket方式建立连接、发布、订阅的功能,方便用户可以直接测试功能效果。 二、client参考mqtt官网提供的几个版本的客户端源码,最终使用了libemqtt版本。直接从github下载代码之后make即可生成发布和订阅两个demo,这里以订阅为例运行sub后,demo死循环打印接收到的订阅的消息通过web发送客户端订阅的主题消息,消息内容为hello, World。这里需要理解下,虽然是通过服务端的后台发送的消息,但web通过websocket建立连接后,此时的web也是客户端之一。客户端接收到消息,并打印出来libemqtt的demo中只是用到了基本的功能,源码并不是很多,但是库中的多个接口还需要日后有时间慢慢研究下","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"IOT","slug":"嵌入式/IOT","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/IOT/"}],"tags":[{"name":"物联网","slug":"物联网","permalink":"https://linzhipeng.top/tags/%E7%89%A9%E8%81%94%E7%BD%91/"},{"name":"MQTT","slug":"MQTT","permalink":"https://linzhipeng.top/tags/MQTT/"}]},{"title":"浅谈内存函数栈中的栈帧(arm)","slug":"linux/内存/浅谈内存函数栈中的栈帧(ARM)","date":"2021-01-31T11:23:20.000Z","updated":"2021-02-12T12:08:50.249Z","comments":true,"path":"2021/01/31/linux/内存/浅谈内存函数栈中的栈帧(ARM)/","link":"","permalink":"https://linzhipeng.top/2021/01/31/linux/%E5%86%85%E5%AD%98/%E6%B5%85%E8%B0%88%E5%86%85%E5%AD%98%E5%87%BD%E6%95%B0%E6%A0%88%E4%B8%AD%E7%9A%84%E6%A0%88%E5%B8%A7(ARM)/","excerpt":"栈是函数调用的实现基础,而栈帧就是函数调用栈中的一个基础知识点。对栈帧理解后能更好的理解函数运行过程。简单来理解,程序运行过程中,PC指针每遇到一个函数,栈都会新增一个栈帧。栈帧记录着函数的参数,返回地址,父函数的栈底指针和自身相关的局部变量等。理解栈帧前先明确以下几个寄存器概念:","text":"栈是函数调用的实现基础,而栈帧就是函数调用栈中的一个基础知识点。对栈帧理解后能更好的理解函数运行过程。简单来理解,程序运行过程中,PC指针每遇到一个函数,栈都会新增一个栈帧。栈帧记录着函数的参数,返回地址,父函数的栈底指针和自身相关的局部变量等。理解栈帧前先明确以下几个寄存器概念: esp,栈顶指针 ebp,栈底指针 pc,指向程序运行的下一行程序地址 注意:ebp指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;esp所指的栈帧顶部和系统栈的顶部是同一个位置。 下面以一个简单的函数调用,对应函数的运行状态总结下栈帧的生命周期。程序的执行顺序如代码注释 12345678910111213int fun(int x, int y){ int c = 4; //4 return c; //5}int main() //1{ int a = 1; int b = 2; //2 fun(a, b); //3 printf("hello world\\n"); //6 return 0; //7} 首先main函数也是被运行库调用的所以,栈会有一个初始状态。两个关键点1、函数调用,参数由右到左依次拷贝压栈,跟新PC指针到fun函数起始地址,更新栈底指针ebp为当前栈顶也就是ebp = esp2、函数返回,更新PC指针为返回地址,更新ebp栈底指针为old esp(上一级的栈底)。跟新esp为栈ebp,销毁函数栈帧。更新sp之后,sp是指向的位置是上图中的程序6地址,此时被调用函数的工作已经全部完成。但是栈没有并没有恢复到与调用函数之前一样,要向恢复占空间还需两步骤。1)调用函数调整PC指针指向返回值地址(也就是程序6的地址),并出栈返回值地址 2)依据调用惯例规定,返回值参数的栈,需要调用者恢复 疑:学习栈帧之前一直有个疑问,栈都是先进后出的,那比如我定义了a和b个局部变量,现在想用a的值,岂不是要先把b出栈???实在不然,栈的先进后出只是对于新增删除而言,一个栈内的变量都是可以通过ebp-x(x带边变量在栈帧中的偏移)来访问的。总结栈帧的组成:参、返、B、局","categories":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"内存","slug":"Linux/内存","permalink":"https://linzhipeng.top/categories/Linux/%E5%86%85%E5%AD%98/"}],"tags":[{"name":"内存","slug":"内存","permalink":"https://linzhipeng.top/tags/%E5%86%85%E5%AD%98/"}]},{"title":"2020不讲武德,2021能否改变","slug":"2020不讲武德,2021能否改变","date":"2021-01-01T11:02:03.000Z","updated":"2021-02-12T12:10:38.245Z","comments":true,"path":"2021/01/01/2020不讲武德,2021能否改变/","link":"","permalink":"https://linzhipeng.top/2021/01/01/2020%E4%B8%8D%E8%AE%B2%E6%AD%A6%E5%BE%B7%EF%BC%8C2021%E8%83%BD%E5%90%A6%E6%94%B9%E5%8F%98/","excerpt":"2020过的真快,可能是因为疫情原因,一年到头没怎么出门,现在回想这一年基本一直在工作。没有什么时间和朋友出去转转的机会。当然离家太远也没什么交心的朋友,也可能是工作之后每年都是这样吧。","text":"2020过的真快,可能是因为疫情原因,一年到头没怎么出门,现在回想这一年基本一直在工作。没有什么时间和朋友出去转转的机会。当然离家太远也没什么交心的朋友,也可能是工作之后每年都是这样吧。 罗列下2020对于我比较重要的事情,和女友分手,企图跳槽失败以告终。这两件事感觉说明了这一年对我确实悲哀之年。分手确实是感觉两个人在一起不合适,无论是家庭,地域,学历,性格等等感觉都不很合适,也没什么多说的。与其浪费她的时间,不如痛快的早点结束。准备跳槽也是2020年初就在躁动的一件事,原因有很多。一是感觉现在的工作环境不适合我,整天都在改bug和移植代码,没有留给自己时间去学习新的知识。二是年初也没有涨薪,不是我小肚鸡肠,主要是这种环境下(感觉干的不开心的情况下),如果在不谈点钱,干活都没劲。当然对我而言(一个双非本科毕业的普通人)来说,毕业能来海康这样的大平台确实还实属幸运,不过心里总归感觉不属于这。先后在各种平台投了小米和字节等我心仪的公司,当然简历可能都没过。最后面试了涂鸦,感觉面试下来感觉体验还不错,最后也发了offer。10月份那个月真实我最纠结的时候,纠结了好久,最终还是和我师父和组长提出了要走的想法,HR和组长先后找我谈话,最终把我心里那团火扑灭了,安心干活吧,拿了年终奖再说。所以,今年没跳槽成功。不过这个跳槽的新路历程也带给了我不少成长。这段坎坷的经历,也给我租房带来入了窘境,今年先后住过四个小区。开始是萧山的龙湖天玺,房子到期后和同事合租到了滨江边的祥云雅苑,后来又被自如解约。解约时,正好在考虑跳槽,考虑到可能换工作不住在这里了,又自己短租了一个月的蛋壳。说来也巧,刚住进半个月蛋壳就报雷了,总共租一个月,停网半个月,押金到现在还没退(虽然只有600),最后也就是现在住的新洲花苑,算是离公司最近的小区了,感觉还不错,如果明年不换工作的话应该会一直住在这里。好的一方面,因为住的离公司近了,养成了早上8.30去公司领体面钱的习惯,还能吃个早饭。吃完早饭8.50左右,然后回工位可以看会书。这个习惯感觉不错,能保证每天都有自己的学习时间,以后也要一直保持下去,还是早睡早起好。展望下2021,先说工作的事情,如果还是这样的工作内容,或者有薪资方面的不符合预期,还是打算跳槽走人,长期目标是回庄里发展。所以既然学不到东西和赚不到钱肯定是会走的。然后是学习目标,还是保证每天读书的习惯。当然,也要丰富自己的其他方面的阅历,所以2021的flag也要定一下总结下2020: 1.分手 2.跳槽失败2021 flag: 1.跳槽(如果有机会尽量能回家) 2.读5本书(必读:程序员的修炼之道,代码大全) 3.爬两座山 4.去一次海,见一条河 5.学习一门乐器(目前是尤克里里) 6.博客网站上半年全部工作完成,成立自己的项目","categories":[],"tags":[]},{"title":"物联网利器——ESP8266(入门及环境搭建)","slug":"embedded/IOT/esp8266_1","date":"2020-12-27T15:16:49.000Z","updated":"2021-02-12T11:48:24.832Z","comments":true,"path":"2020/12/27/embedded/IOT/esp8266_1/","link":"","permalink":"https://linzhipeng.top/2020/12/27/embedded/IOT/esp8266_1/","excerpt":"早就听说过,8266这款MCU,B站上也看多过好多人用这个板子DIY自己的作品,所以前段时间从某宝购入了一款全引脚引出的8266开发板,核心板本身就集成WIFI和Flash,整个板子除了核心板和串口转USB芯片,没别的东西,看起来比较小巧,当然还有比这更小的只有核心板的芯片,不过那种没有几个IO口可用,某宝也是有卖的。","text":"早就听说过,8266这款MCU,B站上也看多过好多人用这个板子DIY自己的作品,所以前段时间从某宝购入了一款全引脚引出的8266开发板,核心板本身就集成WIFI和Flash,整个板子除了核心板和串口转USB芯片,没别的东西,看起来比较小巧,当然还有比这更小的只有核心板的芯片,不过那种没有几个IO口可用,某宝也是有卖的。 1.开发环境搭建买板子送的官方资料,指定的编译IDE是好像是Eclipse,烧录还要使用另一个软件。环境搭建比较繁琐。看到过B站上有人使用arduino的IDE开发,编译和烧录可以都在这个环境完成。所以我也是用来arduinoIDE。环境搭建也比较简单。 下载IDE:直接去官网下载对应系统版本压缩包https://www.arduino.cc/en/Main/Software下载的是个压缩包,解压后直接运行目录下的exe文件就行,无需安装 环境配置运行软件 文件->首选性->附加开发板管理地址 修改为http://arduino.esp8266.com/stable/package_esp8266com_index.json之后保存。工具->开发板->开发板管理器开发板管理器配置页最下面会多出8266的相关选项,选择最新版本后点击安装相关的库文件。(下载可能会很慢,链接的是github,所以可能巨慢,下图是已经下载完成之后)下载完成之后工具->开发板 下面就会多了8266板子的信息,选择对应的板子型号就可以(我的是ESP8266 Module,下载程序还会对Flash大小进行设置,比如我的板子是32Mb对应的应该选择4MB)3.程序编译和烧录使用官方的示例程序,烧录到板子文件->示例->ESP8266->Blink该程序是官方LED闪烁例程,在点击->符号开始编译并烧录,过程可能大概不到10秒中之后,程序烧录进板子,核心板的等间隔闪烁,说明程序烧录成功。","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"IOT","slug":"嵌入式/IOT","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/IOT/"}],"tags":[{"name":"单片机","slug":"单片机","permalink":"https://linzhipeng.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"},{"name":"物联网","slug":"物联网","permalink":"https://linzhipeng.top/tags/%E7%89%A9%E8%81%94%E7%BD%91/"}]},{"title":"8266驱动spi屏ST7735小结","slug":"embedded/IOT/esp8266_2","date":"2020-12-27T15:16:49.000Z","updated":"2021-02-12T11:50:41.817Z","comments":true,"path":"2020/12/27/embedded/IOT/esp8266_2/","link":"","permalink":"https://linzhipeng.top/2020/12/27/embedded/IOT/esp8266_2/","excerpt":"ESP8266+arduino确实是一个神奇的组合,很适合新人入手,不过对于从51或者32转型的过来人,使用起来颇有些崴脚。首先是开源库的存在,依托arduino的开源,在IDE或者github有很多的开源库使用,我猜测现有的一些驱动,估计都能找到驱动库,具体怎么找可能就要自己花费些时间了。","text":"ESP8266+arduino确实是一个神奇的组合,很适合新人入手,不过对于从51或者32转型的过来人,使用起来颇有些崴脚。首先是开源库的存在,依托arduino的开源,在IDE或者github有很多的开源库使用,我猜测现有的一些驱动,估计都能找到驱动库,具体怎么找可能就要自己花费些时间了。 前情提要: 导线连接: ESP8266——————–TFTGPIO4——————–RSTGPIO5——————–D/CGPIO13(MOSI)———–DIN(SDI,MOSI)GPIO14(SCK)————-CLK(SCK)GPIO15(SS,CS)———–CS(SS) 3.3V———————-3.3V,LED+100R电阻GND———————GND(括号里的为同一个io不同称呼)ESP8266引脚名称对应关系参考下图 接入ST7735将下面两个库放到自己环境的对应目录下两个库的源码和我的APP代码压缩包已经上传到我的github git@github.com:zip-link/8266_ST7735tft.git 可以直接参考我的APP(我的也是抄的别人的),APP实现的功能是webserver+1.8寸tft显示图片。tft屏幕的图片可以在web重新上传,当然上传的图片格式也是有要求的,只是实现的bmp格式,而且像素大小必须和屏幕保持一致。比如我的屏幕是128160的,所以上传的图片必须是128160像素的。 完全编译APP的代码是1.8寸tft, 如果你的屏幕是1.4的可以在APP代码中改动参考7533库的对应的你的屏幕尺寸的宏就可以 APP实现的整个工作流程:8266连接路由器–>8266建立web服务器–>电脑访问web页面–>上传图片–>保存图片到flash–>读取图片数据–>发送个给tft显示 贴下最终的实现效果 开源库的使用为了使用8266快速接入某个模块,首先可以找到对应的驱动库,可能比较耗费时间,github和百度。比如我这次接入的ST7735百度了很长时间,尝试了多次,才找到合适的。正常的库文件一般都是自带例程的。需要将库解压到libraries下,之后就可以在,IDE中找到了。例程中一般是基础功能的展示,依次烧录下可以更加熟悉下整个模块的功能。 在.h参考模块对外API,自己的代码中引用对应的.即可使用相关接口。","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"IOT","slug":"嵌入式/IOT","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/IOT/"}],"tags":[{"name":"单片机","slug":"单片机","permalink":"https://linzhipeng.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"},{"name":"物联网","slug":"物联网","permalink":"https://linzhipeng.top/tags/%E7%89%A9%E8%81%94%E7%BD%91/"}]},{"title":"基础学习总结——线程池","slug":"coding/线程池/基础学习总结——线程池","date":"2020-12-27T15:16:13.000Z","updated":"2021-02-12T12:01:15.975Z","comments":true,"path":"2020/12/27/coding/线程池/基础学习总结——线程池/","link":"","permalink":"https://linzhipeng.top/2020/12/27/coding/%E7%BA%BF%E7%A8%8B%E6%B1%A0/%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%E2%80%94%E2%80%94%E7%BA%BF%E7%A8%8B%E6%B1%A0/","excerpt":"经常听到线程池的概念,不过在实际工作和项目里没有应用过线程池。线程池可能是作为服务端的开发经常用到的技术架构,所以最近我也通过网络简单的学习了下线程池的概念,稍微理解了线程池的应用场景。 在高并发的服务环境下,存在多个客户端访问服务端相同服务的场景,而传统方式,是为多客户端创建一一对应的处理线程,这在高并发场景下极大的耗费服务端的资源。为了解决这类问题,服务端可使用线程池,为同一服务创建相同的线程队列,这些线程循环处理工作队列中的任务,当有客户端需要这种服务时将增加任务到工作队列中。","text":"经常听到线程池的概念,不过在实际工作和项目里没有应用过线程池。线程池可能是作为服务端的开发经常用到的技术架构,所以最近我也通过网络简单的学习了下线程池的概念,稍微理解了线程池的应用场景。 在高并发的服务环境下,存在多个客户端访问服务端相同服务的场景,而传统方式,是为多客户端创建一一对应的处理线程,这在高并发场景下极大的耗费服务端的资源。为了解决这类问题,服务端可使用线程池,为同一服务创建相同的线程队列,这些线程循环处理工作队列中的任务,当有客户端需要这种服务时将增加任务到工作队列中。 在实际使用上,可能大家只需要知道如何向工作队列中添加任务即可,不过理解线程池其中的处理流程,可以在遇到问题时更快的理解和定位问题所在,下面通过自己写的一个S端的线程池创建的demo,详细介绍下S端的线程池框架。 首先,前面提到过的,使用线程池需要:线程队列,工作队列,线程池节点。下面是三个对应的结构体 1234567891011121314151617181920212223242526272829303132333435363738394041//工作队列节点struct JOB{ void (*job_func)(void * args); int date; struct JOB* pNext; struct JOB* pPrev; };//线程队列节点struct WORKER{ pthread_t tid; struct WORKER* pNext; struct WORKER* pPrev; };//线程池节点struct Manager{ struct WORKER* pWorkers; struct JOB* pJobs; pthread_mutex_t mtx; pthred_cond_t cond;};/*使用宏定义,实现链表的增加删除节点*/#define LL_ADD(item, list) \\{ item->pNext = list; list->pPrev = item; list = item;}#define LL_DEL(item, list) \\{ if(item == list) item->pNext = item->pPrev = NULL; item->pPrev->pNext = item->pNext; item->pNext->pPrev = item->pPrev;} 初始线程池,包括线程池参数和化线程队列 1234567891011121314151617181920//线程池初始化int CreatNthreads(Manager* pMgPool, int workerNum){ int i = 0; if(NULL == pMgPool) { return -1; } pMgPool->mtx = PTHREAD_MUTEX_INITIALIZER; pMgPool->cond = PTHREAD_COND_INITIALIZER; for(i = 0; i < workerNum; i++) { WORKER* pWorker = NULL; malloc(pWorker,sizeof(WORKER)); pthread_creat(&pWorker->tid, NULL, TaskWorkerCallback, pMgPool); LL_ADD(pWorker, pMgPool->pWorkers); } } 实现线程池内部线程接口的逻辑 123456789101112131415161718192021222324//线程处理回调void *TaskWorkerCallback(Manager* pMgPool){ JOB* pJob = NULL; if(NULL == pMgPool) { return -1; } while(1) { memset(pJob, 0, sizeof(Job)); pthread_mutex_lock(&pMgPool->mtx); while(NULL == pMgPool->pJobs) pthread_cond_wait(&pMgPool->cond ,&pMgPool->mtx); pJob = pMgPool->pJobs; if(pJob) LL_DEL(pJob,pMgPool->pJobs); pthread_mutex_unlock(&pMgPool->mtx); pJob->job_func(pJob->date); pJob = NULL; free(pJob); } } 前端需要调用postJob()接口向后端工作队列添加节点 123456789101112131415//工作真正的处理接口void func(){}//向工作队列添加节点void postJob(Manager* pMgPool,int date){ JOB* pJob = NULL; malloc(pJob,sizeof(JOB)); pthread_mutex_lock(&pMgPool->mtx); pJob->job_func = func; pJob->date = date; LL_ADD(pJob,pMgPool->pJobs); pthread_mutex_unlock(&pMgPool->mtx); pthread_cond_signal(&pMgPool->cond);} 以上方式实现的工作队列链表是“类栈链表”,及后来的节点,却优先处理。和栈的出栈入栈方式类似,我这里先叫“类栈链表”吧,到这里基本上实现了线程栈的创建和使用流程,具体在根据应用场景,实现func即可; 缺点:如果是需要响应C端的服务,使用线程池可能会影响响应速度。(不需要响应的,比如客户端发起的会导致写硬盘的操作) 优点:高并发场景下,极大降低了S端的性能需求 (以上,均为作者个人理解,如有错误支持,欢迎评论指导)","categories":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"线程池","slug":"Linux/线程池","permalink":"https://linzhipeng.top/categories/Linux/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"线程池","slug":"线程池","permalink":"https://linzhipeng.top/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"}]},{"title":"应用开发中常用的进程线程通信","slug":"linux/应用开发中常用的进程线程通信","date":"2020-08-23T21:29:13.000Z","updated":"2021-02-12T11:58:30.356Z","comments":true,"path":"2020/08/23/linux/应用开发中常用的进程线程通信/","link":"","permalink":"https://linzhipeng.top/2020/08/23/linux/%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1/","excerpt":"进程通信是学习应用编程的基础,尤其是对于大型嵌入式项目而言,熟悉常用的通信方式是能写好代码和能看懂代码的前提。其实在大学刚接触linux就学习过进程间的几种通信方式,不过由于当时接触的项目都比较简单,导致没能对这几种通信有比较好的理解和应用。工作之后有了大量的代码阅读量,感觉对几种通信的应用有了新的认识,所以坐下简单的总结。 共享内存 信号量 消息队列 管道","text":"进程通信是学习应用编程的基础,尤其是对于大型嵌入式项目而言,熟悉常用的通信方式是能写好代码和能看懂代码的前提。其实在大学刚接触linux就学习过进程间的几种通信方式,不过由于当时接触的项目都比较简单,导致没能对这几种通信有比较好的理解和应用。工作之后有了大量的代码阅读量,感觉对几种通信的应用有了新的认识,所以坐下简单的总结。 共享内存 信号量 消息队列 管道 共享内存共享内存是几种通信方式中常用的程序间通信方式容易理解的。(如果不理解共享内存这个名词,你可直接把他想象成常用标志位,不过这个标志位可能会是个很大的结构体或数组)这里对共享内存的介绍分为进程间和线程间两部分总结。 线程间线程间的共享内存是最简单的,因为相同进程共享全局和静态变量,所以只要定义全集变量,即可实现多个线程共享内存实现数据通信了。 进程间由于各个进程的内存空间是独立的,所以全局变量那一套是走不通的。这时需要使用共享存储,共享存储允许多个进程共享一个特定的内存缓冲区。常用的接口如下:shmget()获取共享内存标识符shmat()虚拟地址转换 (接口只做了简单的总结,具体的函数原型,可以借助工具书和百度) 以常用的客户进程与服务进程为例:C进程,S进程C调用S对外接口get_shm_addr(),S的get_shm_addr()需要实现内存的申请,获取共享内存标识符,虚拟地址转换,最后把转换后的地址返回给C,C和S就可以通过共享存储的方式通信了。 注意:不管是线程还是进程间的通信,都需要C-S两端控制缓冲区的互斥使用,C去写数据时,S也去写数据如何处理?这里就需要不同的应用场景不同的控制了。一般通过信号量或自行定义读写指针辅佐控制。 posix信号量posix信号量与之对应的是XSI信号量,课本上讲的就是XSI信号量。不过posix信号量作为后来者,无论在性能和使用规范上都明显优于后者。所以这里也是简单介绍下posix的信号量。(信号量通俗理解,简单的可以真的可以理解成一个可以在进程间共享的标志位,不过这个标志位是需要使用系统接口才能对标志位进行加减)常用到的接口包括创建信号量,sem_open()关闭信号量,关闭信号量对应的fd ,sem_close()删除信号量,sem_unlink()发送信号量,对信号量进行+1操作sem_post()接受信号量,对信号量进行-1操作sem_wait() (接口只做了简单的总结,具体的函数原型,可以借助工具书和百度) sem_wait是阻塞接受,还有直接返回,和超时接受两个接口。常用的信号量是二进制信号量,即信号量是非一即零的标志。与之对应的还有计数信号量。信号量是何种类型是在信号量创建时确定的。 posix消息队列和信号量一样,队列这里也只总结常用的posix消息队列,XSI消息队列不做总结。消息队列可以说是线程间通信中,最常用到的通信方式之一了。常用的接口创建队列mq_open()关闭队列mq_close()删除队列mq_unlink()发送消息mq_send()接受消息mq_receive() (接口只做了简单的总结,具体的函数原型,可以借助工具书和百度) 常用的消息队列使用模式是,多个C线程向同一个S线程发送消息。例如C1、C2作为单独处理线程,S作为网络通信线程,C1、C2可以都通过同样的消息队列,发送消息给S实现数据上行。 管道 管道分为无名管道和有名管道,无名管道可以用作主子进程之间的通信。有名管道也就是常说的FIFO,可作为任意进程间的通信。 (通俗理解,管道和文件读写一样,可以将管道看做一个文件,比较特殊的是他有读写描述符是不一样的) 由于管道通信与文件读写基本一样,这里不做其他说明。","categories":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"应用开发","slug":"Linux/应用开发","permalink":"https://linzhipeng.top/categories/Linux/%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"进程通信","slug":"进程通信","permalink":"https://linzhipeng.top/tags/%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1/"}]},{"title":"linux开机自启脚本","slug":"linux/内核","date":"2020-08-16T20:57:59.000Z","updated":"2021-02-12T11:55:33.978Z","comments":true,"path":"2020/08/16/linux/内核/","link":"","permalink":"https://linzhipeng.top/2020/08/16/linux/%E5%86%85%E6%A0%B8/","excerpt":"系统初始化完成后会运行初始化脚本 rc.local,想要实现开机自动启动某个功能,可以在这个脚本增加些执行命令。 rc.local/rc.sysinit,一般为路径为/etc/rc.d/rc.local或/etc/rc.d/rc.sysinit 可以直接在脚本退出之前执行某个脚本或执行某个shell命令","text":"系统初始化完成后会运行初始化脚本 rc.local,想要实现开机自动启动某个功能,可以在这个脚本增加些执行命令。 rc.local/rc.sysinit,一般为路径为/etc/rc.d/rc.local或/etc/rc.d/rc.sysinit 可以直接在脚本退出之前执行某个脚本或执行某个shell命令 update-rc.d 自启通过update-rc.d xxx defaults NN命令(NN为启动顺序) 命令将xxx脚本放到初始化执行的队列中去,以实现自启。NN代表的是初始化优先级,如果脚本需要用到网络等延迟服务,NN需设置一个比较大的数字,如90,保证脚本启动前所依赖服务已经启动。 与这个命令配合使用的是sudo update-rc.d xxx remove,是把xxx脚本或功能移除初始化队列 开,关,重启功能的脚本实现开发的某个新程序xxx,如果实现 /etc/int.d/xxx start|stop|restart 脚本控制开关,会方便平时的管理和开机启动 这里对这个脚本常用实现方法简单总结下: 1 将可执行程序xxx放到 /usr/local/bin目录(并修改执行权限) 12sudo mv xxx /usr/local/bin/xxxsudo chmod +x /usr/local/bin/xxx 2 在/etc/int.d/目录下编写xxx的控制脚本,复制如下脚本即可 123456789101112131415161718192021222324252627282930313233#!/bin/sh -eNAME=xxxDAEMON=/usr/local/bin/$NAMEPIDFILE=/var/run/$NAME.pid[ -x "$DAEMON" ] || exit 0case "$1" in start) if [ -f $PIDFILE ]; then echo "$NAME already running..." echo -e "\\033[1;35mStart Fail\\033[0m" else echo "Starting $NAME..." start-stop-daemon -S -p $PIDFILE -m -b -o -q -x $DAEMON -- param || return 2 echo -e "\\033[1;32mStart Success\\033[0m" fi ;; stop) echo "Stoping $NAME..." start-stop-daemon -K -p $PIDFILE -s TERM -o -q || return 2 rm -rf $PIDFILE echo -e "\\033[1;32mStop Success\\033[0m" ;; restart) $0 stop && sleep 2 && $0 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 ;;esacexit 0 需要将NAME改变成程序的名称,param是程序执行需要的参数,没有参数可以删除调param 完成上面两步之后即可通过/etc/int.d/xxx start|stop|restart 命令控制功能开关。","categories":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"内核应用","slug":"Linux/内核应用","permalink":"https://linzhipeng.top/categories/Linux/%E5%86%85%E6%A0%B8%E5%BA%94%E7%94%A8/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"内核","slug":"内核","permalink":"https://linzhipeng.top/tags/%E5%86%85%E6%A0%B8/"}]},{"title":"linux开机自启脚本","slug":"Server/ajax/内核","date":"2020-08-16T20:57:59.000Z","updated":"2021-02-12T11:55:33.978Z","comments":true,"path":"2020/08/16/Server/ajax/内核/","link":"","permalink":"https://linzhipeng.top/2020/08/16/Server/ajax/%E5%86%85%E6%A0%B8/","excerpt":"系统初始化完成后会运行初始化脚本 rc.local,想要实现开机自动启动某个功能,可以在这个脚本增加些执行命令。 rc.local/rc.sysinit,一般为路径为/etc/rc.d/rc.local或/etc/rc.d/rc.sysinit 可以直接在脚本退出之前执行某个脚本或执行某个shell命令","text":"系统初始化完成后会运行初始化脚本 rc.local,想要实现开机自动启动某个功能,可以在这个脚本增加些执行命令。 rc.local/rc.sysinit,一般为路径为/etc/rc.d/rc.local或/etc/rc.d/rc.sysinit 可以直接在脚本退出之前执行某个脚本或执行某个shell命令 update-rc.d 自启通过update-rc.d xxx defaults NN命令(NN为启动顺序) 命令将xxx脚本放到初始化执行的队列中去,以实现自启。NN代表的是初始化优先级,如果脚本需要用到网络等延迟服务,NN需设置一个比较大的数字,如90,保证脚本启动前所依赖服务已经启动。 与这个命令配合使用的是sudo update-rc.d xxx remove,是把xxx脚本或功能移除初始化队列 开,关,重启功能的脚本实现开发的某个新程序xxx,如果实现 /etc/int.d/xxx start|stop|restart 脚本控制开关,会方便平时的管理和开机启动 这里对这个脚本常用实现方法简单总结下: 1 将可执行程序xxx放到 /usr/local/bin目录(并修改执行权限) 12sudo mv xxx /usr/local/bin/xxxsudo chmod +x /usr/local/bin/xxx 2 在/etc/int.d/目录下编写xxx的控制脚本,复制如下脚本即可 123456789101112131415161718192021222324252627282930313233#!/bin/sh -eNAME=xxxDAEMON=/usr/local/bin/$NAMEPIDFILE=/var/run/$NAME.pid[ -x "$DAEMON" ] || exit 0case "$1" in start) if [ -f $PIDFILE ]; then echo "$NAME already running..." echo -e "\\033[1;35mStart Fail\\033[0m" else echo "Starting $NAME..." start-stop-daemon -S -p $PIDFILE -m -b -o -q -x $DAEMON -- param || return 2 echo -e "\\033[1;32mStart Success\\033[0m" fi ;; stop) echo "Stoping $NAME..." start-stop-daemon -K -p $PIDFILE -s TERM -o -q || return 2 rm -rf $PIDFILE echo -e "\\033[1;32mStop Success\\033[0m" ;; restart) $0 stop && sleep 2 && $0 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 ;;esacexit 0 需要将NAME改变成程序的名称,param是程序执行需要的参数,没有参数可以删除调param 完成上面两步之后即可通过/etc/int.d/xxx start|stop|restart 命令控制功能开关。","categories":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"内核应用","slug":"Linux/内核应用","permalink":"https://linzhipeng.top/categories/Linux/%E5%86%85%E6%A0%B8%E5%BA%94%E7%94%A8/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"内核","slug":"内核","permalink":"https://linzhipeng.top/tags/%E5%86%85%E6%A0%B8/"}]},{"title":"一种基于ajax的动态网页框架设计方法","slug":"Server/一种基于ajax的动态网页框架设计方法","date":"2020-08-16T20:57:59.000Z","updated":"2021-02-12T12:06:19.279Z","comments":true,"path":"2020/08/16/Server/一种基于ajax的动态网页框架设计方法/","link":"","permalink":"https://linzhipeng.top/2020/08/16/Server/%E4%B8%80%E7%A7%8D%E5%9F%BA%E4%BA%8Eajax%E7%9A%84%E5%8A%A8%E6%80%81%E7%BD%91%E9%A1%B5%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%B3%95/","excerpt":"ajax由于其可以做到不用重新加载整个页面而获取后台数据刷新局部网页,而广泛为web开发中应用。后台管理系统一般都是左侧或上方存在导航栏,固定不变,其余部分作为真正的数据部分。","text":"ajax由于其可以做到不用重新加载整个页面而获取后台数据刷新局部网页,而广泛为web开发中应用。后台管理系统一般都是左侧或上方存在导航栏,固定不变,其余部分作为真正的数据部分。 如上图,功能是点击导航栏中的1或2标签可以实现内容DIV的对应功能显示。传统的设备方案是使用iframe标签将整个页面分成两个部分,做到互不影响。但是又因为两个页面的独立性不能做到整体页面显示的统一控制。这时候想到使用ajax做刷新内容解决问题。1、将各个功能内容页面的HTML独立加到对应的HTML文件中,例如导航1->1.html;2、为导航1添加onchange js事件changefun;3、changefun接口内部做ajax请求,请求1.html的所有内容。后端需实现1.html全部文件的html组装返回。ajax回调接口将内容div的内容清空,然后将1.html内容全部回填到内容div;4、如果内容div的内容都是静态数据,那么上一步做完,即可完成功能。如果内容div需要动态从数据库获取数据,还需做下一步;5、新建js refreshfun接口,以ajax形式获取动态内容html。同样后端需实现对应动态内容的数据库访问和html组装。changefun回调接口,在回填内容div之后调用刷新数据接口refreshfun,刷新内容div中的动态数据。6、首页的内容div如果也是动态的同样可以通过ajax形式获取。通过js 的页面加载初始化接口调用ajax方案可实现加载页面时直接加载出首页全部内容。 以上可以作为一种新的后台框架设计方案。","categories":[{"name":"服务器","slug":"服务器","permalink":"https://linzhipeng.top/categories/%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"name":"ajax","slug":"服务器/ajax","permalink":"https://linzhipeng.top/categories/%E6%9C%8D%E5%8A%A1%E5%99%A8/ajax/"}],"tags":[{"name":"ajax","slug":"ajax","permalink":"https://linzhipeng.top/tags/ajax/"}]},{"title":"代码规范——全局变量使用规范","slug":"coding/coding1","date":"2020-08-09T15:41:40.000Z","updated":"2021-02-12T12:03:14.274Z","comments":true,"path":"2020/08/09/coding/coding1/","link":"","permalink":"https://linzhipeng.top/2020/08/09/coding/coding1/","excerpt":"最近参与的项目有大量的开发量,功能调试过程中发现自己在使用全局变量时引入了严重的问题,导致程序崩溃。回想下自己开发时使用全局变量,十有八九都会引起各种问题。所以想着专门记录下全局变量的使用规范和注意事项,防止以后还会出现同样的问题。","text":"最近参与的项目有大量的开发量,功能调试过程中发现自己在使用全局变量时引入了严重的问题,导致程序崩溃。回想下自己开发时使用全局变量,十有八九都会引起各种问题。所以想着专门记录下全局变量的使用规范和注意事项,防止以后还会出现同样的问题。 一、声明和初始化声明时最好是在开发设计到的模块文件进行声明,对每个变量定义资源锁,使用时必须获取线程锁资源。 在模块内部封装初始化、获取、配置(init, get, set)三个接口,如全局变量是数组要对数组的索引进行有效性验证。全局变量定义最好是使用static静态声明,如果其他模块使用的话,三个接口可以对外提供,这样保证三个接口的正确使用,就可以保证全局变量的使用规范。 二、内存使用如果全局变量数据量小,可直接使用栈内存,在声明时直接初始化使用。 如果全局变量是数据量比较大的数组或结构体,建议使用堆内存,也就是动态申请的内存。例如:声明定义一个指针,然后在init接口malloc对应数据量的内存。 总之,数据量大的最好动态申请内存,有两个优点: 一是全局全量是长期占用不释放的一块内存,如果线程本身的占内存空降并不是恨大,大量的占用大量占内存,可能会导致某些压力情况下,导致线程崩溃。 二是考虑到内存越界情况出现时的排查,若是使用的栈内存,可能只会导致某个线程启动时存在线程内部的内存越界,从而导致某个功能异常或不能用。但是如果使用的是堆内存,由于堆内存是进程共享使用,错误的使用会直接导致进程崩溃,这样能尽快复现问题和排查问题。 总结: 1.全局变量使用要加锁 2.对外提供三接口,接口内部做有效性判断 3.数据量大的使用动态内存","categories":[{"name":"编码","slug":"编码","permalink":"https://linzhipeng.top/categories/%E7%BC%96%E7%A0%81/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"全局变量","slug":"全局变量","permalink":"https://linzhipeng.top/tags/%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F/"}]},{"title":"搭建自己的嵌入式博客服务器(五)Mysql+PHP实现动态博客网站","slug":"embedded/blogServer/blogServer5","date":"2020-06-21T21:43:21.000Z","updated":"2021-02-12T11:41:52.717Z","comments":true,"path":"2020/06/21/embedded/blogServer/blogServer5/","link":"","permalink":"https://linzhipeng.top/2020/06/21/embedded/blogServer/blogServer5/","excerpt":"按照前面的博客搭建步骤,可以在本地搭建一个看起来还不错的个人博客,但是总感觉有些不足。一个是网页都只是“静态的”,也就是说博客里的每一个网页都是事先在本地早就生生成好的,按照hexo的机制,每次更新一个博客都要至少新增两个文件.md文件和一个新的index.html文件,虽然两个文件本身不是很大,但总是感觉有些不友好;另外一个也是最让我难受的就是跟新博文的方式,只能通过markdown的方式新增文件,然后重新生成整个hexo工程,过程很是繁琐。为了解决这两个问题,我决定对博客进行大换血,使用数据库方式存储博文,PHP作为博客的后台处理程序,实现博客的展示,同样实现一个简单的后台管理页面方便我可以随时随地的管理博客内容。","text":"按照前面的博客搭建步骤,可以在本地搭建一个看起来还不错的个人博客,但是总感觉有些不足。一个是网页都只是“静态的”,也就是说博客里的每一个网页都是事先在本地早就生生成好的,按照hexo的机制,每次更新一个博客都要至少新增两个文件.md文件和一个新的index.html文件,虽然两个文件本身不是很大,但总是感觉有些不友好;另外一个也是最让我难受的就是跟新博文的方式,只能通过markdown的方式新增文件,然后重新生成整个hexo工程,过程很是繁琐。为了解决这两个问题,我决定对博客进行大换血,使用数据库方式存储博文,PHP作为博客的后台处理程序,实现博客的展示,同样实现一个简单的后台管理页面方便我可以随时随地的管理博客内容。 一、mysql数据库的选用我用过的数据库也就是sql server和mysql了,sql server是window环境下大家常用的数据库,因为有专门的图形化数据库管理工具,所以用起来也是比较简单,功能也比较强大。对比mysql来说,mysql没有官方的数据库管理工具,但是却有小巧灵活的特点,我的本地环境使用linux环境,所以也是选用了mysql作为数据库,为了方便简单的操作,我在window安装了Navicat Premium,可以通过IP的形式远程和图形化管理liunx下面的数据库。mysql的安装方式,这里就不再详细总结了,CSDN上搜一下,有详细的步骤和配置说明。 二、PHP的简单入门学习之前在学校的的时候学习过javeEE,jsp之类的一些东西,所以说到搭建动态网页的时候首先想到的是用java搭建,不过当时只是在windows下面搭建的编程和运行环境,如何在linux下面的搭建呢?确实是个问题,在网上找了许久,找到的方法是先在win下面将调试好的程序导出包,把包放到linux对应的java目录下,感觉应该会有更好的方案,不过感觉应该会比较麻烦,最终放弃了使用java的方案。听过PHP是最好的语言,但是一直没有接触过,所以想试下用PHP代替java,网上搜索了下,都说是CI框架比较简单,适合初学者。最终也是使用了PHP的CI框架实现。下面简单记录下CI的几个点: config目录config目录文件是项目的CI配置文件,config/datebase.php可配置项目连接的数据库,ip、用户名、密码等; model目录model目录是数据库处理文件,使用model类与数据库的数据表对应,新建一个表就在model下面新建一个对应的model类,并在对应的类实现底层的数据库操作函数; view目录view目录是前端网页; controllers目录controllers目录是项目的业务处理层,相当于java的serverlet;新增一个页面,都要在controllers目录新增一个对应的业务处理类,因为CI框架的机制是不允许直接访问view目录的网页,访问资源都要先访问controllers目录的资源,由controllers的类加载页面。当然controllers也要处理前端通过post或get方式提交的数据。 libraries目录libraries目录是库文件目录,有框架的库定义的类,如果想自定义一个类也要放到这个目录下新增; CI框架的绝对目录重定向由于config.php的默认设置,用户访问时会自定定向到ip/index.php同样浏览器请求资源时的URL也会自动加上inde.php/ 的前缀,这个配置给开发时设置资源访问和链接提交路径时带来了很大的不便,可以通过改变config.php的下面两行12$config['base_url'] = 'locahost';$config['index_page'] = ''; 在CI的根目录下,即在index.php,system的同级目录下,建立.htaccess填充代码如下123RewriteEngine onRewriteCond $1 !^(index\\.php|css|js|img|fonts|editormd|robots\\.txt)RewriteRule ^(.*)$ /index.php/$1 [L] 之后在每个view下的文件添加1<base href = "<?php echo site_url();?>"> 对应的页面就能通过“/”访问到项目路径下的资源了。这样设置之后不仅可以解决上面的问题,而且RewriteCond设置的对应的目录下的资源也可以直接通过URL访问到。三、从hexo到动态网站这部分其实就是将原来hexo生成的静态HTML文件,填充php代码和删除冗余的html内容。对于不熟悉前端的新手来说可能有一定的难度。拿主页为例,主要替换的就是原来的博文部分,博文部分的HTML结构都是一样的,不过是每个博文的标题内容是不同的。通过PHP循环业务层传过来的数组,完成对每个博文的内容赋值即可。12345678910<?phpforeach ($pages as $page){?> <p style="font-size: 14px;> <?php echo $page->content;?> </p><?php}?> HTML文本其实比较简单,不过讲解起来也不是一两句话能说清楚的,这里就不详细讲解了,建议有需要的同学还是简单学习下相关的知识,毕竟熟悉之后就可以随意的改动你所看到的页面了。","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}]},{"title":"菜鸟入门github上传本地项目","slug":"github/githubCommit","date":"2020-02-08T08:10:45.000Z","updated":"2021-02-11T08:23:38.401Z","comments":true,"path":"2020/02/08/github/githubCommit/","link":"","permalink":"https://linzhipeng.top/2020/02/08/github/githubCommit/","excerpt":"首先通过github管理你的项目的前提是你要有一个github账户并且你的电脑或者服务器已经安装了git以下按提交资源到github的先后顺序介绍几个git命令","text":"首先通过github管理你的项目的前提是你要有一个github账户并且你的电脑或者服务器已经安装了git以下按提交资源到github的先后顺序介绍几个git命令 1.首先需要初始化git文件 1git init 执行成功后,你的项目目录下会生成一个.git的隐藏文件。 2.然后可以通过add命令添加新增文件到本地的github缓存 1git add . //这里使用. 代表所有文件,当然也可以添加特定的单个或多个文件 3.对比本地资源的提交差异 1git status 该命令会把你本地工作区和暂存区的版本进行比较,查看当前的状态。我下面的状态是已经把所有文件加入到了暂存区中,但是还没有提交到本地历史区。 4.提交差异 1git commit -m "这里是注释。。。" 该命令会把本地暂存区中的文件提交到本地历史区,注意只有在本地历史区中的内容才能提交到github。执行该命令后,我们所有的文件都只是在本地。没有github任何关系。 5.提交本地差异到github的仓库暂存区 1git remote add origin github项目URL 6.先同步github文件 1git pull --rebase origin master 如果对比commit的文件与服务器文件有差异,这条命令会先将差异打包,之后同步,最后将差异合并。 7.提交之前暂缓区的文件到github 1git push -u origin master 这条命令执行完之后,就可以在github上看到提交或者改动的代码了","categories":[{"name":"github","slug":"github","permalink":"https://linzhipeng.top/categories/github/"}],"tags":[{"name":"github","slug":"github","permalink":"https://linzhipeng.top/tags/github/"},{"name":"代码库","slug":"代码库","permalink":"https://linzhipeng.top/tags/%E4%BB%A3%E7%A0%81%E5%BA%93/"}]},{"title":"搭建自己的嵌入式博客服务器(四)hexo安装及部署到GitHub","slug":"embedded/blogServer/blogServer4","date":"2020-02-06T07:07:54.000Z","updated":"2021-02-11T07:42:56.814Z","comments":false,"path":"2020/02/06/embedded/blogServer/blogServer4/","link":"","permalink":"https://linzhipeng.top/2020/02/06/embedded/blogServer/blogServer4/","excerpt":"到了博客搭建的最后一个阶段,使用hexo框架搭建完全静态博客站点,这里还简单介绍下将博客资源部署到github上的方法。","text":"到了博客搭建的最后一个阶段,使用hexo框架搭建完全静态博客站点,这里还简单介绍下将博客资源部署到github上的方法。 一,hexo的安装使用1.安装Hexo 1npm install -g hexo-cli 之后使用hexo -v查看一下版本,版本显示成功则安装成功。 2.使用首先要初始化整个模板 1hexo init myblog myblog是你的的博客文件夹的名称,根据自己的喜好设置 更新npm 1npm install 这里简单介绍下初始化好的博客文件夹下的几个目录 node_modules: 相关的依赖不常用 public:存放生成的页面 scaffolds:生成文章的一些模板,比如使用hexo new “xx”,xx.md的默认内容可以通过修改模板来改变 source:用来存放你的文章就是 xx.md文件 themes:存放主题 常用命令接介绍 1234hexo clean //清楚原有的静态缓存hexo generate //重新生成所有的静态网页hexo deploy //部署到相关的平台 例如 githubhexo new "xx" //生成xx.md,新建文章 二,部署到GitHub(可以跳过,部署之后方便备份)1,安装github(linux)1sudo apt-get install git 用git –version 来查看一下版本,显示版本代表安装成功。 2, 部署博客到GitHub1.新建仓库登录GitHub.com,点击New repository,新建仓库创建一个和你用户名相同的仓库,仓库名称为 “用户名+.github.io”,这样你的博客才能通过GitHub page访问,GitHub page是GitHub新建项目后为项目建立的私人网站站点,通过 用户名+.github.io 访问时,会显示主目录下的index.html 之后create repository,创建完成。 2.生成SSH添加到GitHub 生成ssh key 12git config --global user.name "用户名"git config --global user.email "邮件地址" 可以用以下两条,检查一下你有没有输对 12git config user.namegit config user.email 确认输入无误后,然后创建SSH 1ssh-keygen -t rsa -C "youremail" 一路回车,过程中会提示输入一些东西,使用默认配置即可,直接回车。 生成的ssh目录下回有id_rsa私钥和id_rsa.pub公钥,需要把这个公钥放在GitHub上,这样当你链接GitHub自己的账户时,公钥私钥匹配成功才能通过git上传你的文件到GitHub上。 在setting中,找到SSH keys的设置选项,点击New SSH key,把你的id_rsa.pub里面的信息复制进去。 1ssh -T [email protected] 提示(You’ve successfully authenticated, but GitHub does not provide shell access),则表示测试成功。 3.将hexo部署到GitHub 打开hexo的根目录配置文件 _config.yml,添加以下内容 1234deploy: type: git repo: https://github.com/YourgithubName/YourgithubName.github.io.git branch: master 要想提交资源到github还需要安装deploy-git ,也就是部署的命令,这样你才能用命令部署到GitHub。 1npm install --save hexo-deployer-git 之后就可以使用hexo deploy /hexo d提交了 第一次提时,可能要你输入username和password。 提交成功后可以通过github查看是否提交成功,确认成功后可以通过用户名+.github.io查看你的博客了(注意这里是https)","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}]},{"title":"手机语音助手控制自己开发的物联网设备方法","slug":"embedded/VoiceAssistant","date":"2020-02-06T03:37:30.000Z","updated":"2021-02-11T08:23:24.703Z","comments":true,"path":"2020/02/06/embedded/VoiceAssistant/","link":"","permalink":"https://linzhipeng.top/2020/02/06/embedded/VoiceAssistant/","excerpt":"市面上可以买到的物联网设备多种多样,而现有的提供商小米,百度,天猫等基本都是各玩各的,没有同意的设备接入控制的标准协议。所以开发者如果自行开发了一款设备没有同意的标准协议可以遵循,除非是调用厂商提供提供的SDK(实际还是按照他们的自定义协议实现)。那么如何简单实现将用siri或者小爱同学等语音助手控制自己的设备呢?下面总结下目前我了解的方法。","text":"市面上可以买到的物联网设备多种多样,而现有的提供商小米,百度,天猫等基本都是各玩各的,没有同意的设备接入控制的标准协议。所以开发者如果自行开发了一款设备没有同意的标准协议可以遵循,除非是调用厂商提供提供的SDK(实际还是按照他们的自定义协议实现)。那么如何简单实现将用siri或者小爱同学等语音助手控制自己的设备呢?下面总结下目前我了解的方法。 首先分两种情况一个是Siri,另外是小爱,小度等国内几个大厂商。 一,Siri苹果系统完全可以借助一款APP“捷径”,这个APP是可以自定义语音指令,然后执行特定的脚本的,这就非常方便了。 比如用的比较多的物联网终端控制器ESP8266,源代码提供通过访问ESP8266的IP加引脚号控制引脚高低电平的方法。例如新建捷径指令“打开灯”,捷径指令执行访问你的ESP8266的ip加引脚号的URL就可以实现控制某个引脚的高低电平,相当于可以控制某个开关信号了,具体收到这个开关信号之后的实现逻辑你就可以自定义实现了。 另外的场景(控制板非ESP8266),这个也是比较常用的到的场景。目前我的解决办法是在自己家搭建一个运行Linux的服务端,开机ssh服务,然后通过捷径,运行脚本登录SSH的方式,直接控制你的Linux服务端执行某个特定的脚本,脚本的内容就可以是具体的控制你的某个设备了。另外的话第二种方法相比第一种还有一个好处就是,你可以为你的服务端绑定域名,这样的话就可以实现远程ssh,这样不必在同一网络内,在任何地方你都可以通过siri控制你的设备了。 二,小爱小度等除了Siri之外,现在用的比较多的就是小爱同学了,想想你自己可以直接通过家里的小爱音箱控制自己开发的设备是不是也不错,这里可以关注下“点灯科技”。简单介绍下这个平台集成了目前使用广泛的语音平台,提供不同厂家的协议SDK,跳过了直接使用原厂商SDK的种种限制,比如接入小爱同学需要你有自己的服务器外,还需要有自己的APP,账号体系等等。 只需要将对应平台的SDK下载到自己的设备,设备本身实现对应的回调函数即可实现语音控制功能,不过从官网看目前该平台提供的免费版仅仅支持控制灯,传感器和插座开关,具体的实现方案点灯的官网也给出了较为详细的步骤。","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"语音助手","slug":"嵌入式/语音助手","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E8%AF%AD%E9%9F%B3%E5%8A%A9%E6%89%8B/"}],"tags":[{"name":"语音助手","slug":"语音助手","permalink":"https://linzhipeng.top/tags/%E8%AF%AD%E9%9F%B3%E5%8A%A9%E6%89%8B/"},{"name":"Siri","slug":"Siri","permalink":"https://linzhipeng.top/tags/Siri/"}]},{"title":"记ST_LINK与STM32的连接调试","slug":"embedded/stm32/embedded-stm32-stlink","date":"2020-01-31T07:50:55.000Z","updated":"2021-02-11T07:43:32.845Z","comments":false,"path":"2020/01/31/embedded/stm32/embedded-stm32-stlink/","link":"","permalink":"https://linzhipeng.top/2020/01/31/embedded/stm32/embedded-stm32-stlink/","excerpt":"由于先前的32板子不能但不调试,就是不支持JTAG/SWD,平时测试程序只能是编译没问题了就直接下载到板子上,如果想看运行过程中的变量的值只能是靠自己的推测,或者一些自己想出来的土办法(比如用串口打印或者led显示)。","text":"由于先前的32板子不能但不调试,就是不支持JTAG/SWD,平时测试程序只能是编译没问题了就直接下载到板子上,如果想看运行过程中的变量的值只能是靠自己的推测,或者一些自己想出来的土办法(比如用串口打印或者led显示)。 于是在某宝30多块入手mini32板子加st-link调试器。如图 客服不是很专业,给了一些资料就完事了,再问一些问题,一直支支支吾吾的答不上来。只能是自己看资料解决了。 下面将连接流程介绍下: 首先保证连线的准确,这是重中之重,一定要检查清楚。 其次安装客服给的ST-link v2驱动程序,安装过程中的选项默认就好包括路径。 安装完成后插上STLINK打开设备管理器就可以看到STLINK的设备驱动了。 现在并不能打开MDK调试,还有另外一步,安装STM32 ST-LINK Utility这里面包括了WIN10 的STlink的驱动解决方案,同样安装过程中的选项默认就好包括路径。 这时如果没有什么大问题就可以用ST-LINK Utility下载可执行文件了,如果可以固件升级却出现这这样的提示: 检查插线和主板,这表示ST-LINK连接没问题,问题是找不到主板。 ST-LINK Utility运行无误后,表明你的硬件没有一点问题,现在就可以打开MDK(keil)了,只需简单几步: 1:打开项目配置选项卡,点击Debug,配置仿真器为ST-LINK DEBUG,点击setting 正常情况下: SW或者选择JATG,右侧SW/JATG Devise 下回出现id号。。。这种情况下直接点解确定 还有可能会是这种情况: 这表示ST-LINK连接无误,而查找不到目标板,请检查插线和主板。 正常后点击Utilities选项卡: 同样设置ST-LINK DEBUG 点击Setting Download Function 选择第一项,其余默认点击下方ADD 选择你的开发板型号。 至此,ST-link调试可以在MDK下下载,单步执行。","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"stm32","slug":"嵌入式/stm32","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/stm32/"}],"tags":[{"name":"stm32","slug":"stm32","permalink":"https://linzhipeng.top/tags/stm32/"},{"name":"单片机","slug":"单片机","permalink":"https://linzhipeng.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"},{"name":"调试器","slug":"调试器","permalink":"https://linzhipeng.top/tags/%E8%B0%83%E8%AF%95%E5%99%A8/"}]},{"title":"stm32硬件I2C实现问题","slug":"embedded/stm32/embedded-stm32-hi2c","date":"2020-01-31T07:34:33.000Z","updated":"2021-02-11T07:43:27.965Z","comments":false,"path":"2020/01/31/embedded/stm32/embedded-stm32-hi2c/","link":"","permalink":"https://linzhipeng.top/2020/01/31/embedded/stm32/embedded-stm32-hi2c/","excerpt":"虽然软件可实现I2C读取三轴传感器数据,但I2C作为一种重要的通信协议是一定要搞清楚问题所在的,SO继续研究之前的问题。(网上传言STM32硬件I2C有问题,但仍然有人实现出来)再次启动程序,依旧是停在原来的位置","text":"虽然软件可实现I2C读取三轴传感器数据,但I2C作为一种重要的通信协议是一定要搞清楚问题所在的,SO继续研究之前的问题。(网上传言STM32硬件I2C有问题,但仍然有人实现出来)再次启动程序,依旧是停在原来的位置 等待EV6,网上搜索相关问题好多人都停在了等待EV5上。分析EV5等待问题,主机发送起始信号,没能接受从设备发送的应答, 或者可能都没有发送。此问题应该是接线或IIC初始化代码的问题。 而我此时停在等待EV6,说明已检测到该设备。换句话说从设备已经知道了主设备的存在。却在主设备发送设备地址之后,接受不到从设备的应答信号,自己分析有两种可能,一是设备地址错误,从设备接受到不是自身的设备而地址自然不会应答。二是从设备已应答,而并接受到。在设备地址正确的前提下(前面已经通过例程验证过,前面的文章),思索第二种问题。 网上类似问题网友回应也是繁多,如RCC时钟初始化的问题,检查代码RCC_APB1Periph_I2C1,RCC_APB2Periph_GPIOB均已使能。又如可能i2c1默认引脚被复用,或该引脚没接上拉电阻,而不能开漏输出。将默认PIN6.PIN7重映射后依旧老样子排除该问题。 (重映射函数 1GPIO_PinRemapConfig(u32 GPIO_Remap, FunctionalState NewState) ) 再一次想到时钟问题,将IIC初始化结构体中的速率该低 原来是400000,也不行 直到在某论坛看到据说可以直接用的程序,打开发现代码并无差别,唯一不同的是,RCC初始化,其代码在主函数开始就初始化了所有的需要用到的时钟包括 ,只是初始化位置不同。。。。 终于豁然开朗。 数据读取准确无误。 之后又将初始化程序恢复到原位置发现,程序仍可正确运行,不知何解。 尝试恢复速率发现,恢复成400000后不能读取,程序停滞在等待EV6 2000000等待EV5,1000000等待EV5 只能还设为100000,可以正常工作。 至此可确定STM32硬件I2C真的有问题!!!! 程序只能在10000输出,仅供参考 .h 12345678910#ifndef __I2C_H__#define __I2C_H__ #include "stm32f10x.h" void I2C_GPIO_Config(void);void I2C1_Init(void);void I2C1_Write(u8 addr, u8 data);u8 I2C1_Read(u8 nAddr);#endif .c 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960#include "tb_delay.h"#include "i2c.h"void I2C_GPIO_Config(){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_Init(GPIOB, &GPIO_InitStructure);} void I2C1_Init(){I2C_InitTypeDef I2C_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);I2C_DeInit(I2C1);I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = 0x77;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = 10000;I2C_Init(I2C1, &I2C_InitStructure);I2C_Cmd(I2C1, ENABLE);} void I2C1_Write(u8 addr, u8 data){I2C_AcknowledgeConfig(I2C1,ENABLE); I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)){;}//EV5I2C_Send7bitAddress(I2C1,0x3a,I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;} //EV6I2C_SendData(I2C1,addr); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;} //EV8I2C_SendData(I2C1,data);while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;} I2C_GenerateSTOP(I2C1,ENABLE); } u8 I2C1_Read(u8 nAddr){I2C_AcknowledgeConfig(I2C1,ENABLE); //????I2C_GenerateSTART(I2C1,ENABLE); //???????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)){;} //??EV5I2C_Send7bitAddress(I2C1,0x3a,I2C_Direction_Transmitter); //????????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){;}//??EV6I2C_SendData(I2C1,nAddr);//?????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){;} //??EV8 I2C_GenerateSTART(I2C1,ENABLE); //???????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)){;} //??EV5I2C_Send7bitAddress(I2C1,0x3a,I2C_Direction_Receiver); //???????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){;} //??EV6I2C_AcknowledgeConfig(I2C1,DISABLE); //??????I2C_GenerateSTOP(I2C1,ENABLE); //???????while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){;} //??EV7return I2C_ReceiveData(I2C1); //???????}","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"stm32","slug":"嵌入式/stm32","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/stm32/"}],"tags":[{"name":"stm32","slug":"stm32","permalink":"https://linzhipeng.top/tags/stm32/"},{"name":"I2C","slug":"I2C","permalink":"https://linzhipeng.top/tags/I2C/"},{"name":"单片机","slug":"单片机","permalink":"https://linzhipeng.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"}]},{"title":"人脸三维建模软件","slug":"3D/3Dmodeling","date":"2020-01-31T07:21:43.000Z","updated":"2021-02-11T08:24:29.773Z","comments":true,"path":"2020/01/31/3D/3Dmodeling/","link":"","permalink":"https://linzhipeng.top/2020/01/31/3D/3Dmodeling/","excerpt":"最近接触了三维打印机,学了两天3Dmax建模,打印了一个简单的手机壳,形状倒是很漂亮不过还是没达到理想的效果。","text":"最近接触了三维打印机,学了两天3Dmax建模,打印了一个简单的手机壳,形状倒是很漂亮不过还是没达到理想的效果。 设想如果打印机能把我的人脸模型打印出来岂不是更完美了,毕竟这都是电影里才有的画面,真要让我完成了,哈哈!!!于是想方设法开始找人脸三维建模软件,找了好多软件有手机APP,也有PC程序。这里简单介绍几个比较好用的软件。 先从简单的开始吧,首先是一个APP,Qlone.可以很方便的直接用手机围着建模物体转几圈就能把模型建出来,而且自己感觉模型的相似度很高。不过前提是你要把物体放在官方提供的二维码上面,软件才能识别出来物体进行建模。同样一个更大的缺点是如果想要导出模型的或需要付费。。。这对我简直是致命的缺点,我也是因此直接卸载。所以不多说了。 第二个是PC端软件,3DSOM Pro v5,软件的官网对软件有着高度的评价,官网也给处理示例或模型。需要对准一个物体进行360的无死角的拍照,最后将所有照片上传到该软件进行建模。按照步骤一步步来,我拍摄了30多张照片,最后软件只识别13张,其余的都作废了,上传完后还要对所有的图片逐个进行边缘的分离,人工处理(很累,费眼神)。经过大约20-30分钟的等待后模型就能建出来了,过程很是曲折,不过模型确实是有点意思,当然不是很完美。3DSOM的建模方法让我想到了科幻电影特效制作过程中,在整个绿色幕布下拍摄建模的方法,感觉很是类似。 该软件的中文资料较少,简单使用步骤在这 这里是官方14天试用版的下载地址 第三个是我最后决定用来人脸建模的软件FaceGen Modeller,在人脸建模方面可以说很好用了,估计在这也是网上能找到的最好用的了吧。这个软件可以直接随机生成人脸也可以根据你的照片生成真人的人脸模型,人脸模型的生成需要人脸的三张照片,正面和左右两面,然后按照软件示例中的人脸对人脸的特征点进行定位,普通电脑经过10左右就能生成人脸模型了。最后出来的模型和真人没什么区别,反正熟人一看就知道是你。人脸建模方面,强烈推荐使用。使用步骤简单不做详细的阐述了。","categories":[{"name":"3D","slug":"3D","permalink":"https://linzhipeng.top/categories/3D/"}],"tags":[{"name":"3D打印","slug":"3D打印","permalink":"https://linzhipeng.top/tags/3D%E6%89%93%E5%8D%B0/"},{"name":"建模","slug":"建模","permalink":"https://linzhipeng.top/tags/%E5%BB%BA%E6%A8%A1/"}]},{"title":"搭建自己的嵌入式博客服务器(三)内网穿透","slug":"embedded/blogServer/blogServer3","date":"2020-01-31T05:53:41.000Z","updated":"2021-02-11T08:23:18.237Z","comments":true,"path":"2020/01/31/embedded/blogServer/blogServer3/","link":"","permalink":"https://linzhipeng.top/2020/01/31/embedded/blogServer/blogServer3/","excerpt":"由于自己的嵌入式设备,是通过自己家的路由器连接接入网络的,而黑心的运营商在我们每次上网时分配给我们的公网IP是变动的。为了实现外网设备访问我们的内网设备,我们不得不使用一个内网穿透穿透工具打通两个设备之间的通信链路。而现有的内网穿透工具参差不齐,下面我简单介绍下我试用的三个,都是免费的。","text":"由于自己的嵌入式设备,是通过自己家的路由器连接接入网络的,而黑心的运营商在我们每次上网时分配给我们的公网IP是变动的。为了实现外网设备访问我们的内网设备,我们不得不使用一个内网穿透穿透工具打通两个设备之间的通信链路。而现有的内网穿透工具参差不齐,下面我简单介绍下我试用的三个,都是免费的。 花生壳优点:自定义域名,不变动缺点:没有使用ARM Ubuntu的程序版本 借鉴前人经验,我首先使用的是众所周知的花生壳做为内网穿透工具。可以在花生壳官方域名前加自定义域名而且域名不会变动。但是没有使用我的环境的版本,官网倒是有树莓派的版本和操作文档,尝试使用树莓派的版本在我的环境下验证失败。不过官方的工单服务和客服回复的倒是挺及时的,使用树莓派的同学倒是可以尝试使用下花生壳。官网链接 Sunny-Ngrok优点:自定义域名,不变动。支持多平台缺点:免费服务器经常崩溃 这个也是前一段时间一直在使用的工具,官方的文档给的很详细,详细到提供开关服务的脚本和自启动,操作也比较简便,不过由于服务器经常崩溃,导致不能正常使用(免费厂崩溃),不过听说付费的服务器也是很稳定的。不差钱的同学可以试下。官网链接 natfrp优点:国外服务器服务稳定,支持多平台缺点:不支持提供域名,需要自己注册域名 这个是最近我才使用的,官方的操作也比较简便,不过官方提供的程序没有给开机自启动和服务开关的脚本,需要自己编写。还有就是需要自己买个域名,在域名解析服务加上natfrp上你注册的服务器的域名。这个使用了两天比较稳定,是我目前正在使用的。官网链接","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}]},{"title":"搭建自己的嵌入式博客服务器(二)环境搭建","slug":"embedded/blogServer/blogServer2","date":"2020-01-31T05:15:17.000Z","updated":"2021-02-11T07:43:05.387Z","comments":false,"path":"2020/01/31/embedded/blogServer/blogServer2/","link":"","permalink":"https://linzhipeng.top/2020/01/31/embedded/blogServer/blogServer2/","excerpt":"本篇简单介绍几个环境的搭建,包括node,Apache, hexo","text":"本篇简单介绍几个环境的搭建,包括node,Apache, hexo node.js和npm这里使用的是Ubantu操作系统,其他Linux系统同理, (npm是node.js的包管理工具,随同node.js一同下载) 安装curl.12sudo apt-get install curl curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - 安装nodejs和npm 12sudo apt-get install nodejs-legacysudo apt-get install npm (1)安装用于安装nodejs的模块n 1sudo npm install -g n (2)通过n模块安装指定的nodejs 123sudo n latestsudo n stablesudo n lts (3)升级npm为最新版本 1sudo npm install npm@latest -g 确认 最后通过node -v ,npm -v确认版本,nodejs版本要在6.3以上才可以 接下来在bin下面创建个软连接 12/usr/local/bin/ln -s /opt/node-v8.11.1-linux-x64/bin/hexo Apache2 下载 12sudo apt-get updatesudo apt-get install apache2 环境配置(1)/etc/apache2/apache2.conf 是主要配置文件(这个文件的末尾可以看到,include了其它所有的配置文件)。(2)/etc/apache2/ports.conf 始终包含在主配置文件中。它用于确定传入连接的侦听端口,默认为80,我们一般都会重新配置新的端口。(3)apache2的默认web目录:/var/www/html。(在/etc/apache2/sites-enabled/000-default.conf 里可以看到这个 DocumentRoot /var/www/html 配置)(4)设置默认主页的配置文件/etc/apache2/mods-enabled/dir.conf(5)进入到/etc/apache2/sites-available目录下,添加ErrorDocument 404 /404.html,在这里,你的404.html要在DocumentRoot 即可实现自定义404页面按照自己的需求,配置上面的几个文件即可 开关 12sudo /etc/init.d/apache2 [ start | stop | restart | status ]service apache2 [ start | stop | restart | status ] 验证在浏览器输入IP:端口号验证HTTP服务是否正常正常则会显示Ubantu系统默认http服务网页 Hexo 安装 1sudo npm install hexo-cli -g 配置并初始化 1234hexo init blogcd blognpm installhexo server 执行完上面几条命令之后,在浏览器打开地址:http://localhost:4000/就会看到hexo为你提供的默认主题,则表示安装成功。到 hexo官网 找个适合自己的主题。下载主题源码到 博客目录/themes/具体的如何更改hexo主题,生成页面和文章可以参考官方文档","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}]},{"title":"搭建自己的嵌入式博客服务器(一)","slug":"embedded/blogServer/blogServer1","date":"2020-01-31T03:41:14.000Z","updated":"2021-02-11T07:43:01.718Z","comments":false,"path":"2020/01/31/embedded/blogServer/blogServer1/","link":"","permalink":"https://linzhipeng.top/2020/01/31/embedded/blogServer/blogServer1/","excerpt":"首先介绍下我搭建自己的博客服务器的背景 前段时间逛知乎时看到某位技术大声,将自己的博客搭建在树莓派上,瞬间引起了我的兴趣,毕竟谁不想拥有一个完全属于自己的博客呀。于是就开始准备了,从网上前人的经验得出现有的搭建方案大致如下:","text":"首先介绍下我搭建自己的博客服务器的背景 前段时间逛知乎时看到某位技术大声,将自己的博客搭建在树莓派上,瞬间引起了我的兴趣,毕竟谁不想拥有一个完全属于自己的博客呀。于是就开始准备了,从网上前人的经验得出现有的搭建方案大致如下: linux –操作系统 Apache –http服务 mysql –数据库管理 PHP –后端编程语言 花生壳 –内网穿透工具 不过由于实现过程中的一些问题,我最终的实现方案是下面这个样子 linux –操作系统 Apache –http服务 Hexo –静态网页生成 Natfrp –内网穿透工具这里还要介绍下我用的开发板 友善NEO 1GRAM 博客的最终的实现效果我的博客接下里我将分章节简单介绍些搭建步骤","categories":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"}]}],"categories":[{"name":"-音视频","slug":"音视频","permalink":"https://linzhipeng.top/categories/%E9%9F%B3%E8%A7%86%E9%A2%91/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"IOT","slug":"嵌入式/IOT","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/IOT/"},{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/categories/Linux/"},{"name":"内存","slug":"Linux/内存","permalink":"https://linzhipeng.top/categories/Linux/%E5%86%85%E5%AD%98/"},{"name":"线程池","slug":"Linux/线程池","permalink":"https://linzhipeng.top/categories/Linux/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"},{"name":"应用开发","slug":"Linux/应用开发","permalink":"https://linzhipeng.top/categories/Linux/%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91/"},{"name":"内核应用","slug":"Linux/内核应用","permalink":"https://linzhipeng.top/categories/Linux/%E5%86%85%E6%A0%B8%E5%BA%94%E7%94%A8/"},{"name":"服务器","slug":"服务器","permalink":"https://linzhipeng.top/categories/%E6%9C%8D%E5%8A%A1%E5%99%A8/"},{"name":"ajax","slug":"服务器/ajax","permalink":"https://linzhipeng.top/categories/%E6%9C%8D%E5%8A%A1%E5%99%A8/ajax/"},{"name":"编码","slug":"编码","permalink":"https://linzhipeng.top/categories/%E7%BC%96%E7%A0%81/"},{"name":"博客服务","slug":"嵌入式/博客服务","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"},{"name":"github","slug":"github","permalink":"https://linzhipeng.top/categories/github/"},{"name":"语音助手","slug":"嵌入式/语音助手","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/%E8%AF%AD%E9%9F%B3%E5%8A%A9%E6%89%8B/"},{"name":"stm32","slug":"嵌入式/stm32","permalink":"https://linzhipeng.top/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F/stm32/"},{"name":"3D","slug":"3D","permalink":"https://linzhipeng.top/categories/3D/"}],"tags":[{"name":"-音视频 -h.264","slug":"音视频-h-264","permalink":"https://linzhipeng.top/tags/%E9%9F%B3%E8%A7%86%E9%A2%91-h-264/"},{"name":"物联网","slug":"物联网","permalink":"https://linzhipeng.top/tags/%E7%89%A9%E8%81%94%E7%BD%91/"},{"name":"MQTT","slug":"MQTT","permalink":"https://linzhipeng.top/tags/MQTT/"},{"name":"内存","slug":"内存","permalink":"https://linzhipeng.top/tags/%E5%86%85%E5%AD%98/"},{"name":"单片机","slug":"单片机","permalink":"https://linzhipeng.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"},{"name":"Linux","slug":"Linux","permalink":"https://linzhipeng.top/tags/Linux/"},{"name":"线程池","slug":"线程池","permalink":"https://linzhipeng.top/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/"},{"name":"进程通信","slug":"进程通信","permalink":"https://linzhipeng.top/tags/%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1/"},{"name":"内核","slug":"内核","permalink":"https://linzhipeng.top/tags/%E5%86%85%E6%A0%B8/"},{"name":"ajax","slug":"ajax","permalink":"https://linzhipeng.top/tags/ajax/"},{"name":"全局变量","slug":"全局变量","permalink":"https://linzhipeng.top/tags/%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F/"},{"name":"Hexo","slug":"Hexo","permalink":"https://linzhipeng.top/tags/Hexo/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://linzhipeng.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"博客服务","slug":"博客服务","permalink":"https://linzhipeng.top/tags/%E5%8D%9A%E5%AE%A2%E6%9C%8D%E5%8A%A1/"},{"name":"github","slug":"github","permalink":"https://linzhipeng.top/tags/github/"},{"name":"代码库","slug":"代码库","permalink":"https://linzhipeng.top/tags/%E4%BB%A3%E7%A0%81%E5%BA%93/"},{"name":"语音助手","slug":"语音助手","permalink":"https://linzhipeng.top/tags/%E8%AF%AD%E9%9F%B3%E5%8A%A9%E6%89%8B/"},{"name":"Siri","slug":"Siri","permalink":"https://linzhipeng.top/tags/Siri/"},{"name":"stm32","slug":"stm32","permalink":"https://linzhipeng.top/tags/stm32/"},{"name":"调试器","slug":"调试器","permalink":"https://linzhipeng.top/tags/%E8%B0%83%E8%AF%95%E5%99%A8/"},{"name":"I2C","slug":"I2C","permalink":"https://linzhipeng.top/tags/I2C/"},{"name":"3D打印","slug":"3D打印","permalink":"https://linzhipeng.top/tags/3D%E6%89%93%E5%8D%B0/"},{"name":"建模","slug":"建模","permalink":"https://linzhipeng.top/tags/%E5%BB%BA%E6%A8%A1/"}]}