-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 155 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 155 KB
1
{"meta":{"title":"kelecn","subtitle":"","description":"kelecn's Blog-Hello World!^_^","author":"kelecn","url":"https://kelecn.top","root":"/"},"pages":[{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-20T09:47:46.000Z","comments":true,"path":"404.html","permalink":"https://kelecn.top/404.html","excerpt":"","text":"404 很抱歉,您访问的页面不存在 可能是输入地址有误或该地址已被删除"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2021-01-13T12:46:10.000Z","comments":true,"path":"about/index.html","permalink":"https://kelecn.top/about/index.html","excerpt":"","text":"关于 您正在与 kelecn 对话中... bot_ui_ini()"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-24T08:50:26.000Z","comments":true,"path":"about/me.html","permalink":"https://kelecn.top/about/me.html","excerpt":"","text":"主要涉及技术:嵌入式开发、电子设计、集成电路、通信、安卓开发、爬虫。一条普通的大四狗联系QQ:1572535194 很惭愧 只做了一点微小的工作 谢谢大家"},{"title":"可乐说","date":"2021-02-09T14:57:41.000Z","updated":"2021-02-09T14:59:12.000Z","comments":true,"path":"bb/index.html","permalink":"https://kelecn.top/bb/index.html","excerpt":"","text":""},{"title":"所有分类","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-20T09:38:44.000Z","comments":true,"path":"categories/index.html","permalink":"https://kelecn.top/categories/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-20T09:40:12.000Z","comments":true,"path":"examples/index.html","permalink":"https://kelecn.top/examples/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-20T09:40:12.000Z","comments":true,"path":"faqs/index.html","permalink":"https://kelecn.top/faqs/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-10-12T11:29:20.000Z","comments":true,"path":"friends/friends.html","permalink":"https://kelecn.top/friends/friends.html","excerpt":"友链","text":"友链 这里可以写友链页面下方的文字备注,例如自己的友链规范、示例等。"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2021-09-26T11:54:03.615Z","comments":true,"path":"friends/index.html","permalink":"https://kelecn.top/friends/index.html","excerpt":"友链","text":"友链 我 kelecn唯爱与科技不可辜负! 主题作者+文档 XAOXUU山重水复疑无路,柳暗花明又一村 Volantis主题文档 朋友们 XU'S BLOG银河小徐 黑石博客人生在勤,不索何获。 W4J1e's blog努力爬上大佬的肩膀 Colsrch愿多年以后,我可以酌一杯清酒,烂醉如泥,梦中回到我们的曾经。 杜老师说师者,传道,授业,解惑! 果果小师弟一个喜欢嵌入式的小码农 云梦 ╮不浅梦,开始的地方 Khighness炒菜K殿下 枋柚梓的猫会发光繁星永存,记忆更古不变。 Thrower的博客梦的挖掘机碾碎黎明的炊烟,冰和雾遮住我的双眼。 小仙女 OnlyIf we dream,everything is possible. 单向链(仅保存一周) 友链规则(必看) ①网站需符合遵守中华人民共和国相关法律法规 ②网站需全局开启https协议 ③友链是双向的,申请前请您先添加本站为友链,不定时回访哦~ 如何添加友链? 🔔 ①Github提交您的友链格式Issues即可!🔔 ②评论区提交您的友链格式即可! 123456site=站点名称url=站点网址screenshot=站点截图avatar=站长头像description=座右铭friendsurl=您的友链页 🔔 我的例子: 123456site=kelecnurl=https://kelecn.top/screenshot=https://cdn.jsdelivr.net/gh/kelecn/images@master/myblog.pngavatar=https://cdn.jsdelivr.net/gh/kelecn/images@master/%E6%82%9F%E7%A9%BA.jpgdescription=唯爱与科技不可辜负!friendsurl=https://kelecn.top/friends/"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2021-08-08T16:49:25.740Z","comments":true,"path":"games/index.html","permalink":"https://kelecn.top/games/index.html","excerpt":"","text":"游戏 2048捉鸡蛋围住小🐱 游戏规则: 🔔 手指向一个方向滑动,所有格子会向那个方向运动。 🔔 相同数字的两个格子,相撞时数字会相加。 🔔 每次滑动时,空白处会随机刷新出一个数字的格子。 🔔 当界面不可运动时(当界面全部被数字填满时),游戏结束;当界面中最大数字是2048时,游戏胜利。 游戏规则: 🔔 控制上下左右键让小狗接住鸡蛋。 🔔 小狗接不住鸡蛋时为输。 🔔 按R刷新游戏。 游戏规则: 🔔 点击小圆点,围住小猫。 🔔 你点击一次,小猫走一次。 🔔 直到你把小猫围住(赢),或者小猫走到边界并逃跑(输)。 在线游戏 小霸王游戏机 POKI H5游戏 畅玩空间 疯狂游戏 PACOGAMES"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-10-20T12:45:24.000Z","comments":true,"path":"image/index.html","permalink":"https://kelecn.top/image/index.html","excerpt":"","text":"相册 壁纸二次元日常旅游"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2020-09-20T09:40:12.000Z","comments":true,"path":"mylist/index.html","permalink":"https://kelecn.top/mylist/index.html","excerpt":"","text":""},{"title":"所有标签","date":"2021-11-05T06:11:33.267Z","updated":"2021-01-11T17:03:16.000Z","comments":true,"path":"tags/index.html","permalink":"https://kelecn.top/tags/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-08-17T15:26:25.151Z","comments":true,"path":"tool/index.html","permalink":"https://kelecn.top/tool/index.html","excerpt":"工具","text":"工具 编程相关 菜鸟教程 W3school 廖雪峰官方网站 书栈网 · BookStack CodeBus 常用工具 文叔叔 Drawio 在线转换 图片压缩-picdiet 去不图床 SM.MS wallhaven 嵌入式相关 Arduino中文社区 野火论坛 开源电子网 微雪课堂 MATLAB中文论坛 前端相关 Iconfont-阿里巴巴矢量图标库"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T08:57:38.000Z","comments":true,"path":"wallpaper/index.html","permalink":"https://kelecn.top/wallpaper/index.html","excerpt":"","text":"相册 内置壁纸 苹果 谷歌 安卓 华为 小米 Samsung OPPO VIVO 一加 魅族 索尼 LG HTC Lenovo ZTE Motorola Nokia Meitu 二次元壁纸 随手拍"},{"title":"","date":"2021-11-05T06:11:33.251Z","updated":"2021-01-07T14:07:48.000Z","comments":true,"path":"smallparts/earth/index.html","permalink":"https://kelecn.top/smallparts/earth/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-03-30T12:26:58.000Z","comments":true,"path":"smallparts/time/index.html","permalink":"https://kelecn.top/smallparts/time/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-03-30T05:10:04.000Z","comments":true,"path":"smallparts/weather/index.html","permalink":"https://kelecn.top/smallparts/weather/index.html","excerpt":"","text":""},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-05T05:14:18.000Z","comments":true,"path":"wallpaper/Android/index.html","permalink":"https://kelecn.top/wallpaper/Android/index.html","excerpt":"","text":"安卓 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-05T05:14:36.000Z","comments":true,"path":"wallpaper/Google/index.html","permalink":"https://kelecn.top/wallpaper/Google/index.html","excerpt":"","text":"谷歌 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-06T08:05:36.000Z","comments":true,"path":"wallpaper/HTC/index.html","permalink":"https://kelecn.top/wallpaper/HTC/index.html","excerpt":"","text":"火腿肠 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T07:22:22.000Z","comments":true,"path":"wallpaper/Lenovo/index.html","permalink":"https://kelecn.top/wallpaper/Lenovo/index.html","excerpt":"","text":"联想 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T07:31:14.000Z","comments":true,"path":"wallpaper/Meitu/index.html","permalink":"https://kelecn.top/wallpaper/Meitu/index.html","excerpt":"","text":"美图 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-05T05:15:10.000Z","comments":true,"path":"wallpaper/Meizu/index.html","permalink":"https://kelecn.top/wallpaper/Meizu/index.html","excerpt":"","text":"魅族 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T07:26:52.000Z","comments":true,"path":"wallpaper/Motorola/index.html","permalink":"https://kelecn.top/wallpaper/Motorola/index.html","excerpt":"","text":"摩托罗拉 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T07:30:04.000Z","comments":true,"path":"wallpaper/Nokia/index.html","permalink":"https://kelecn.top/wallpaper/Nokia/index.html","excerpt":"","text":"诺基亚 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-06T08:07:16.000Z","comments":true,"path":"wallpaper/Sony/index.html","permalink":"https://kelecn.top/wallpaper/Sony/index.html","excerpt":"","text":"索尼 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-27T06:22:46.000Z","comments":true,"path":"wallpaper/Apple/index.html","permalink":"https://kelecn.top/wallpaper/Apple/index.html","excerpt":"","text":"苹果 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-06T08:33:44.000Z","comments":true,"path":"wallpaper/LG/index.html","permalink":"https://kelecn.top/wallpaper/LG/index.html","excerpt":"","text":"LG 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~ LG 放弃手机业务,世界失去了一个充满想象力的厂商https://www.ithome.com/0/544/199.htm"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-05T05:15:32.000Z","comments":true,"path":"wallpaper/OPPO/index.html","permalink":"https://kelecn.top/wallpaper/OPPO/index.html","excerpt":"","text":"绿厂 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-05T05:15:20.000Z","comments":true,"path":"wallpaper/OnePlus/index.html","permalink":"https://kelecn.top/wallpaper/OnePlus/index.html","excerpt":"","text":"一加 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-05T05:16:14.000Z","comments":true,"path":"wallpaper/VIVO/index.html","permalink":"https://kelecn.top/wallpaper/VIVO/index.html","excerpt":"","text":"蓝厂 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-27T07:24:22.000Z","comments":true,"path":"wallpaper/ZTE/index.html","permalink":"https://kelecn.top/wallpaper/ZTE/index.html","excerpt":"","text":"中兴 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-05T05:15:42.000Z","comments":true,"path":"wallpaper/Samsung/index.html","permalink":"https://kelecn.top/wallpaper/Samsung/index.html","excerpt":"","text":"三星 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.282Z","updated":"2021-04-05T08:20:06.000Z","comments":true,"path":"wallpaper/Xiaomi/index.html","permalink":"https://kelecn.top/wallpaper/Xiaomi/index.html","excerpt":"","text":"小米 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"},{"title":"","date":"2021-11-05T06:11:33.267Z","updated":"2021-04-05T05:15:00.000Z","comments":true,"path":"wallpaper/Huawei/index.html","permalink":"https://kelecn.top/wallpaper/Huawei/index.html","excerpt":"","text":"华为 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~"}],"posts":[{"title":"深圳文和友","slug":"31、打卡《深圳文和友》","date":"2021-08-15T15:49:14.000Z","updated":"2021-08-24T15:25:09.781Z","comments":true,"path":"posts/2042/","link":"","permalink":"https://kelecn.top/posts/2042/","excerpt":"简介:深圳文和友在深圳(东门)附近,介于广深铁路和布吉河之间,往返的和谐号动车与绿皮火车、“科技之城”与“市井文化”,过去与未来、生活与艺术在此交织,颇有赛博朋克、怀旧复古之感。","text":"简介:深圳文和友在深圳(东门)附近,介于广深铁路和布吉河之间,往返的和谐号动车与绿皮火车、“科技之城”与“市井文化”,过去与未来、生活与艺术在此交织,颇有赛博朋克、怀旧复古之感。 Your browser does not support the video tag. 一、游玩建议1、节假日人超级多,去了基本就是人挤人,建议错峰出行。 2、打卡圣地茶颜悦色需要到现场扫码抢票(整点抢),一个人只能买两杯,周三有第二杯半价优惠。 3、茶颜悦色我去的是三楼那家,其实一楼还有一家,一楼的人可能会少一点。 4、建议下午去,太阳还没下山之前去到那里,可以看到比较震撼的3D显示外墙。 5、离开时,可以去地铁的马路对面,那里位置特别好。 二、打卡 读万卷书,行万里路。","categories":[{"name":"生活","slug":"生活","permalink":"https://kelecn.top/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"旅行","slug":"旅行","permalink":"https://kelecn.top/tags/%E6%97%85%E8%A1%8C/"},{"name":"打卡","slug":"打卡","permalink":"https://kelecn.top/tags/%E6%89%93%E5%8D%A1/"},{"name":"深圳","slug":"深圳","permalink":"https://kelecn.top/tags/%E6%B7%B1%E5%9C%B3/"},{"name":"深圳文和友","slug":"深圳文和友","permalink":"https://kelecn.top/tags/%E6%B7%B1%E5%9C%B3%E6%96%87%E5%92%8C%E5%8F%8B/"}]},{"title":"给博客添加一个聊天机器人—Botui.js","slug":"30、给博客添加一个聊天机器人—Botui.js","date":"2021-08-12T15:13:14.000Z","updated":"2021-08-20T16:37:03.195Z","comments":true,"path":"posts/32834/","link":"","permalink":"https://kelecn.top/posts/32834/","excerpt":"简介:Botui.js 是一个用于创建对话 UI 的开源 JavaScript 框架,可以用于我们的博客美化。","text":"简介:Botui.js 是一个用于创建对话 UI 的开源 JavaScript 框架,可以用于我们的博客美化。 聊天机器人效果 ^-^ 具体效果看:关于 - kelecn 开源项目:Botuihttps://github.com/botui/botui 一、Botui 简介Botui 是一个有趣的聊天 JS 框架,界面样式像是一个对话聊天界面,使用者可以创建一个对话内容与访客进行互动,互动起来很有趣😋😋😋😋,可自定义程度高,使用难度也不高。下面我就针对 Volantis hexo博客主题进行部署演示。 二、部署 Botui 聊天机器人首先,你可以参考 Botui 进行其他博客系统、主题、网站进行部署,我这边仅提供 Volantis hexo博客主题的部署演示,其实都大差不差,可以参考一下。 下载 botui.js ,我这边提供我使用的,各位直接修改即可; 将下载好的 botui.js 复制到…\\themes\\volantis\\source\\js 路径下; 在你想调用 botui 聊天机器人的文章(index.md)里对应的位置添加以下代码,即可。 123456789<!-- https://www.bootcdn.cn/botui/ --><link href="https://cdn.bootcss.com/botui/0.3.9/botui-theme-default.css" rel="stylesheet"><link href="https://cdn.bootcss.com/botui/0.3.9/botui.min.css" rel="stylesheet"> <bot-ui></botui><script src="/js/botui.js"></script><script>bot_ui_ini()</script> 我的-关于页 markdown文档全文配置如下:有需要朋友的可以参考一下。 12345678910111213141516171819202122232425262728<!-- 我的-关于markdown文档配置 -->---layout: docsseo_title: 关于bottom_meta: falsesidebar: []valine: placeholder: 有什么想对我说的呢?---{% p center logo large red, 关 %}{% p center logo large blue, 于 %}<!-- https://www.bootcdn.cn/botui/ --><link href="https://cdn.bootcss.com/botui/0.3.9/botui-theme-default.css" rel="stylesheet"><link href="https://cdn.bootcss.com/botui/0.3.9/botui.min.css" rel="stylesheet">{% raw %}<!-- 因为vue和botui更新导至bug,现将对话移至js下的botui中配置 --><div class="entry-content"> <div id="kelecnbot" class="popcontainer" style="min-height: 0px; padding: 2px 6px 4px; background-color: rgba(255, 255, 255, 0.5); border-radius: 10px;"> <center>您正在与&nbsp;kelecn&nbsp;对话中...</center> <bot-ui></botui> </div><iframe src="https://kelecn.top/donate/simple/" style="overflow-x:hidden;overflow-y:hidden; border:0xp none #fff; min-height:240px; width:100%;" frameborder="0" scrolling="no"></iframe></div><script src="/js/botui.js"></script><script>bot_ui_ini()</script>{% endraw %} 三、修改 Botui 聊天机器人配置Botui 聊天机器人的预设配置在…\\themes\\volantis\\source\\js\\botui.js,也就是刚才下载的botui.js文件。 这个一看就懂,我就不详细解释了,要注意一下中文支持需要使用UTF-8编码格式,否则可能会乱码。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139//略部分配置代码/* * botui 0.3.4 * A JS library to build the UI for your bot * https://botui.org * * Copyright 2017, Moin Uddin * Released under the MIT license.*///略部分配置代码/* * BotUI回复配置,这个一看就懂,我就不详细解释了,要注意一下中文支持需要使用UTF-8编码格式,否则可能会乱码。 */var botui = new BotUI("kelecnbot");botui.message.bot({ delay: 200, content: "Hi, 陌生人👋👋👋"}).then(function() { return botui.message.bot({ delay: 1000, content: "这里是 kelecn 😋😋😋" })}).then(function() { return botui.message.bot({ delay: 1000, content: "一个热爱技术的蓝孩子~😉😉😉" })}).then(function() { return botui.action.button({ delay: 1500, action: [{ text: "然后呢? 😃😃😃", value: "and" }, { text: "少废话! 🙄🙄🙄", value: "gg" }] })}).then(function(res) { if (res.value == "and") { other() } if (res.value == "gg") { return botui.message.bot({ delay: 1500, content: "  " }) }}); var other = function() { botui.message.bot({ delay: 1500, content: "😘😘😘" }).then(function() { return botui.message.bot({ delay: 1500, content: "目前在从事嵌入式、物联网、硬件方面的工作🐧🐧🐧" }) }).then(function() { return botui.message.bot({ delay: 1500, content: "略懂HTML/CSS/JavaScript/Java,专攻C/C++/Python" }) }).then(function() { return botui.message.bot({ delay: 1500, content: "日常兴趣:旅行、爬山、美食" }) }).then(function() { return botui.message.bot({ delay: 1500, content: "喜欢动手,热爱学习新知识,热爱开源文化,目前正在嵌入式的道路上探索中💪💪💪" }) }).then(function() { return botui.action.button({ delay: 1500, action: [{ text: "为什么叫 kelecn 呢?🤔🤔🤔", value: "next" }] }) }).then(function(res) { return botui.message.bot({ delay: 1500, content: "因为很喜欢《刺客伍六七》里可乐这个角色,于是我就沿用了kele下来,嗯!" }) }).then(function (res) { return botui.message.add({ delay: 1500, type: 'embed', content: 'https://cdn.jsdelivr.net/gh/kelecn/images@master/kele.gif' }); }).then(function(res) { return botui.message.bot({ delay: 1500, content: "emmmmm,至于cn嘛,中国国家顶级域名2333" }) }).then(function() { return botui.action.button({ delay: 1500, action: [{ text: "怎样可以联系你呢?(ง •_•)ง", value: "next" }] }) }).then(function(res) { return botui.message.bot({ delay: 1500, content: "博客评论区留言或者是左下角的对话窗口留言都可以联系我哦~" }) }).then(function(res) { return botui.message.bot({ delay: 1500, content: "欢迎联系我交流哦~🤗🤗🤗" }) }).then(function(res) { return botui.message.bot({ delay: 1500, content: "有需要交换友链的朋友,欢迎点击友链页提交哦~😋😋😋" }) }).then(function(res) { return botui.message.bot({ delay: 1500, content: "那么,仔细看看我的博客吧? ^_^" }) }).then(function() { return botui.message.bot({ delay: 3000, content: "还有,欢迎关注我的 [GitHub](https://github.com/kelecn)哦 !🤗🤗🤗" }) }).then(function() { return botui.message.bot({ delay: 1500, content: " [](https://github.com/kelecn) " }) });} 四、特别感谢特别感谢 Botui 项目的大力支持,喜欢的朋友,欢迎去其Github点点小星星~ Botuihttps://github.com/botui/botui","categories":[{"name":"美化","slug":"美化","permalink":"https://kelecn.top/categories/%E7%BE%8E%E5%8C%96/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"Volantis","slug":"Volantis","permalink":"https://kelecn.top/tags/Volantis/"},{"name":"Botui","slug":"Botui","permalink":"https://kelecn.top/tags/Botui/"},{"name":"聊天机器人","slug":"聊天机器人","permalink":"https://kelecn.top/tags/%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA/"},{"name":"JavaScript","slug":"JavaScript","permalink":"https://kelecn.top/tags/JavaScript/"}]},{"title":"基于OpenMV的无人驾驶智能小车模拟系统","slug":"29、基于OpenMV的无人驾驶智能小车模拟系统","date":"2021-07-29T09:47:47.000Z","updated":"2021-07-31T08:00:44.000Z","comments":true,"path":"posts/51495/","link":"","permalink":"https://kelecn.top/posts/51495/","excerpt":"简介:基于机器视觉模块OpenMV采集车道、红绿灯、交通标志等模拟路况信息,实现一辆能车道保持、红绿灯识别、交通标志识别、安全避障以及远程WiFi控制的多功能无人驾驶小车。","text":"简介:基于机器视觉模块OpenMV采集车道、红绿灯、交通标志等模拟路况信息,实现一辆能车道保持、红绿灯识别、交通标志识别、安全避障以及远程WiFi控制的多功能无人驾驶小车。 一、项目简介 项目地址:OpenMV-autodrivehttps://github.com/kelecn/OpenMV-autodrive 编程软件: 硬件模块 编程软件 OpenMV 使用OpenMV官方的OpenMV IDE ESP8266 使用Arduino官方的Arduino IDE STM32 使用ARM官方的Keil uVision5(ARM版) 功能介绍: 硬件模块 功能实现 OpenMV 主要是利用OpenMV进行路况信息(红绿灯、交通标志、车道)的采取,以及和STM32的通信,具体看OpenMV文件夹。 ESP8266 主要是利用ESP8266与手机端进行远程的指令接收和数据交互,以及和STM32的通讯,具体看ESP8266文件夹。 STM32 主要是通过ESP8266接收远程控制指令和处理路况信息,并根据这些指令数据进行实时的PID控制小车运动。具体看STM32文件夹。 欢迎各位去点点小星星哦~ 二、硬件系统本项目《基于OpenMV的无人驾驶智能小车模拟系统》,主要依靠机器视觉模块OpenMV通过图像处理的方式获取实时的路况信息,以及超声波传感器获取障碍物距离信息,得到的路况数据再通过串口传输到主控器STM32上面,STM32会将实时的路况信息处理成智能小车的运动控制指令,让智能小车实现红绿灯识别、交通标志识别以及车道实时保持的功能,还有STM32也会通过WiFi模块ESP8266与手机端进行路况数据和控制指令的远程交互。硬件系统框图如下: 下面简单介绍一下,整个系统用到的硬件模块。 硬件模块 型号 主控器 STM32F103ZET6(正点原子精英板F103) 视觉模块 OpenMV4 H7 PLUS(STM32H750VBT6 +OV7725) 超声波传感器 HC-SR04 WiFi模块 ESP8266 执行器 双路 H 桥电机驱动、直流电机、开发板自带的LED和蜂鸣器 控制平台 安装Blinker APP的安卓/苹果手机 其他 电源、模型车、导线若干等 具体的硬件电路连接框图如下: 三、软件系统1、OpenMV中的路况识别算法实现本项目的主要路况数据信息都是基于OpenMV摄像头获取的图像进行图像处理得到的。要实现智能小车的自动驾驶行为,最起码要让小车识别到红绿灯、交通标志以及车道,后续主控器才能根据这些路况数据信息控制小车的运动。关于机器视觉模块OpenMV,之前我在《初探机器视觉模块OpenMV》里面已经介绍过了,这里不再赘述。 ①红绿灯识别主要是对摄像头每帧拍摄到的图像进行图像进行阈值处理,再进行判断出现的究竟是哪种红绿灯(红灯、绿灯、黄灯),然后再将这个判定结果和其他两个数据一起打包通过串口发送出去。 【程序流程图】 【主要程序】 123456789101112131415161718192021222324252627282930313233###################################开始####################################...sensor.set_pixformat(sensor.RGB565) # 图片格式设为 RGB565彩色图light_threshold = [(59, 100, 26, 127, -128, 127),(59, 100, -128, -40, -128, 127)]; #设置红绿灯阈值,默认为0无红绿灯 1红灯 2绿灯 4黄灯...#定义寻找色块面积最大的函数def find_max(blobs): max_size=0 for blob in blobs: if blob.pixels() > max_size: max_blob=blob max_size = blob.pixels()return max_blob#主循环while(True): clock.tick() #追踪两个snapshots()之间经过的毫秒数 img = sensor.snapshot() #拍一张照片,返回图像 blobs = img.find_blobs(light_threshold,area_threshold=150); #找到红绿灯 cx=0;cy=0;LED_color=0; #变量定义 if blobs: max_b = find_max(blobs); #如果找到了目标颜色 img.draw_rectangle(max_b[0:4]) #在Blob周围绘制一个矩形 #用矩形标记出目标颜色区域 img.draw_cross(max_b[5], max_b[6]) # cx, cy img.draw_cross(160, 120) # 在中心点画标记 #在目标颜色区域的中心画十字形标记 cx=max_b[5]; cy=max_b[8]; img.draw_line((160,120,cx,cy), color=(127)); img.draw_string(cx, cy, "(%d, %d)"%(cx,cy), color=(127));LED_color=cy; #红绿灯的阈值是数组里的cy(二进制)个print(LED_color); #串行终端打印出 红绿灯序号数据###################################结束#################################### ②交通标志识别主要是利用NCC(Normalized Cross Correlation)归一化互相关算法来进行交通标志的图像识别与匹配。 【NCC算法】 NCC算法的基本实现原理:主要是通过求两幅大小相近的图像的相关系数矩阵来判别两幅图像是否相关。假设需要识别的初始图片$g$的大小为$m×n$,摄像头拍摄到的图片S的大小为$M×N$,其中的以$(x,y)$为左上角点与$g$大小相同的子图像为$S_{(x,y)}$。具体的利用NCC算法实现计算图像相似度的方法如下: $\\rho{(x,y)}$的定义为:随机变量$X$、$Y$的相关系数 $$\\rho{(x,y)}=\\frac{\\sigma(S_{x,y},g)}{\\sqrt[]{D_{x,y}D}}$$ 式中:$\\sigma(S_{x,y},g)$是$S_{x,y}$和$g$的协方差; $D_{x,y}=\\frac {1}{mn}\\sum_{i=1}^{m} \\sum_{j=1}^{n}{(S_{x,y}(i,j)-\\overline S_{x,y})^2}$为$S_{x,y}$的方差; $D=\\frac {1}{mn}\\sum_{i=1}^{m} \\sum_{j=1}^{n}{(g(i,j)-\\overline g)^2}$为$g$的方差; $\\overline g$为$g$的灰度均值; $\\overline S_{x,y}$为$S_{x,y}$的灰度均值; 将$D_{x,y}$和D代入$\\rho{(x,y)}$,会有: $$\\rho{(x,y)=\\frac{ \\frac {1}{mn}\\sum_{i=1}^{m} \\sum_{j=1}^{n}{(S_{x,y}(i,j)-\\overline S_{x,y})(g(i,j)-\\overline g)}}{\\sqrt[]{\\frac {1}{mn}\\sum_{i=1}^{m} \\sum_{j=1}^{n}{(S_{x,y}(i,j)-\\overline S_{x,y})^2}}\\sqrt[]{\\frac {1}{mn}\\sum_{i=1}^{m} \\sum_{j=1}^{n}{(g(i,j)-\\overline g)^2}}}}$$其中,相关系数$\\rho{(x,y)}$满足:$\\rho{(x,y)\\le1}$。 $\\rho{(x,y)}$越接近1,说明两幅图像越接近,也就是大图像的子集中越有可能包含有小图像。通过选取恰当的相关系数,我们就可以认为,相关系数大于该设定值的图像为所需识别的图像,也就是可以实现交通标识的识别。 【程序流程图】 【主要程序】 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051###################################开始####################################...sensor.set_pixformat(sensor.GRAYSCALE) #设置图片格式为灰度图#导入图片模板template1 = image.Image("/1.pgm") #直行template2 = image.Image("/2.pgm") #向右转弯template3 = image.Image("/3.pgm") #向左转弯template4 = image.Image("/4.pgm") #停车让行template5 = image.Image("/5.pgm") #鸣喇叭#主循环while (True): clock.tick() img = sensor.snapshot() flag=0ratio=0#匹配1.pgm(直行)串行终端打印go,flag=1 r1 = img.find_template(template1, 0.70, step=4, search=SEARCH_EX) if r1: img.draw_rectangle(r1,color=(255,0,0)) print("go") flag=1 img.draw_string(10, 10, "%.d"%flag)#匹配2.pgm(向右转弯)串行终端打印right,flag=2 r2 = img.find_template(template2, 0.70, step=4, search=SEARCH_EX) if r2: img.draw_rectangle(r2,color=(0,255,0)) print("right") flag=2 img.draw_string(10, 10, "%.d"%flag)#匹配3.pgm(向左转弯)串行终端打印left,flag=3 r3 = img.find_template(template3, 0.70, step=4, search=SEARCH_EX) if r3: img.draw_rectangle(r3,color=(255,0,0)) print("left") flag=3 img.draw_string(10, 10, "%.d"%flag)#匹配4.pgm(停车让行)串行终端打印stop,flag=4 r4 = img.find_template(template4, 0.70, step=4, search=SEARCH_EX) if r4: img.draw_rectangle(r4,color=(255,255,0)) print("stop") flag=4 img.draw_string(10, 10, "%.d"%flag)#匹配5.pgm(鸣喇叭)串行终端打印beep,flag=5 r5 = img.find_template(template5, 0.70, step=4, search=SEARCH_EX) if r5: img.draw_rectangle(r5,color=(255,255,0)) print("beep") flag=5 img.draw_string(10, 10, "%.d"%flag)###################################结束#################################### ③车道识别 主要通过OpenMV模块,识别并跟踪车道阈值,通过几何运算出小车与车道中线的角度(偏左为正、偏右为负),反馈出小车与车道的真实偏离情况(可量化),后续用于PID控制。 【程序流程图】 【主要程序】 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657###################################开始####################################...sensor.set_pixformat(sensor.RGB565) # 图片格式设为 RGB565彩色图road_threshold = [(23, 0, -45, 19, -31, 28)]; #黑线道路ROI = (0, 100, 320, 40)...#省略了识别车道边框函数#偏移角度计算算法def get_direction(left_blob, right_blob): # 根据图像中的三块左中右的白色部分,计算出摄像头偏转角度 # ratio < 0 左拐,小车在车道偏右位置 # ratio > 0 右拐,小车在车道偏左位置 MAX_WIDTH = 320 # 调节theta来设置中间宽度的比重, theta越高ratio越靠近0 # 需要根据赛道宽度与摄像头高度重新设定到合适大小 theta = 0.01 # 这里的b是为了防止除数是0的情况发生, 设定一个小一点的值 b = 3 x1 = left_blob.x() - int(0.5 * left_blob.w()) #左边黑线中心x值 x2 = right_blob.x() + int(0.5 * right_blob.w()) #右边黑线中心x值#车道信息计算 w_left = x1 #左边车道外宽度 w_center = math.fabs(x2 - x1) #车道中心x值 w_right = math.fabs(MAX_WIDTH - x2) #右边车道外宽度#计算摄像头偏移角度 direct_ratio = (w_left + b + theta * w_center) / (w_left + w_right + 2 * b + 2 * theta * w_center) - 0.5#返回摄像头偏移角度return direct_ratio#省略了可视化绘图函数...while(True): #主循环clock.tick() #追踪两个snapshots()之间经过的毫秒数 img = sensor.snapshot() #拍一张照片,返回图像 blobs = img.find_blobs(road_threshold, roi=ROI, merge=True); a=0;ratio=0; if blobs: left_blob, right_blob = get_top2_blobs(blobs) if(left_blob == None or right_blob == None): print("Out Of Range") continue else:#画出车道左边线 img.draw_rectangle(left_blob.rect()) img.draw_cross(left_blob.cx(), left_blob.cy())#画出车道右边线 img.draw_rectangle(right_blob.rect()) img.draw_cross(right_blob.cx(), right_blob.cy())#可视化显示偏转角度 direct_ratio = get_direction(left_blob, right_blob) draw_direct(img,direct_ratio) ratio=int(math.degrees(direct_ratio)) #偏转角度转成弧度值 img.draw_string(10, 10, "%.d"%ratio) #帧缓冲区实时画出偏转角度 print(ratio) #串行终端打印偏转角度 img.draw_rectangle(ROI,color=(255, 0, 0)) #画出感兴趣区域###################################结束#################################### 2、基于ESP8266的远程控制平台实现主要是利用点灯科技-Blinker物联网平台搭建控制APP的UI界面,以及调用Blinker的控制代码,实现智能小车控制指令的下发与路况数据的上传。 【远程控制平台UI界面】 【UI配置代码】 直接使用 点灯.blinker APP导入配置代码即可获得和我一样的UI布局。 1{¨config¨{¨headerColor¨¨transparent¨¨headerStyle¨¨dark¨¨background¨{¨img¨¨assets/img/headerbg.jpg¨¨isFull¨«}}¨dashboard¨|{¨type¨¨btn¨¨ico¨¨fad fa-arrow-alt-up¨¨mode¨É¨t0¨¨前进¨¨t1¨¨文本2¨¨bg¨Ì¨cols¨Ë¨rows¨Ë¨key¨¨btn-go¨´x´Ì´y´Ï¨speech¨|÷¨clr¨¨#076EEF¨}{ßAßBßC¨fad fa-arrow-alt-down¨ßEÉßF¨后退¨ßHßIßJÌßKËßLËßM¨btn-back¨´x´Ì´y´¤CßO|÷ßPßQ¨lstyle¨É}{ßAßBßC¨fad fa-arrow-alt-right¨ßEÉßF¨右转¨ßHßIßJÌßKËßLËßM¨btn-right¨´x´Ï´y´ÒßO|÷ßPßQßUÉ}{ßAßBßC¨fad fa-arrow-alt-left¨ßEÉßF¨左转¨ßHßIßJÌßKËßLËßM¨btn-left¨´x´É´y´ÒßO|÷ßPßQßUÉ}{ßAßBßC¨fad fa-power-off¨ßEÉßF¨停车¨ßHßIßJÌßKËßLËßM¨btn-stoping¨´x´Ï´y´ÏßO|÷ßPßQßUÉ}{ßA¨tex¨ßF¨😋小车远程监控系统😋¨ßH´´ßJËßC´´ßKÍßLÊßM´´´x´Ë´y´ËßO|÷ßPßQßUÊ}{ßA¨num¨ßF¨障碍物距离¨ßC¨fad fa-route¨ßPßQ¨min¨É¨max¨¢1c¨uni¨¨cm¨ßJÉßKÍßLËßM¨num-distance¨´x´É´y´¤EßO|÷ßUÊ}{ßAßgßF¨小车偏移角度¨ßC¨fad fa-tachometer-alt-fast¨ßPßQßjÉßkº0ßl´º´ßJÉßKÍßLËßM¨num-angle¨´x´Í´y´¤EßO|÷ßUÊ}{ßAßgßF¨红绿灯(红1绿2)¨ßC¨fad fa-siren-on¨ßPßQßjÉßkËßl´´ßJÉßKËßLËßM¨num-led¨´x´É´y´ÏßO|÷ßUÉ}{ßA¨deb¨ßEÉßJÉßKÑßLÌßM¨debug¨´x´É´y´ÌßO|÷ßUÉ}{ßAßgßF¨WIFI信号¨ßC¨fad fa-signal-4¨ßP¨#389BEE¨ßjÉßkº0ßl¨dbm¨ßJÉßKËßLËßM¨num-wifi¨´x´Ï´y´ÉßO|÷ßUÉ}{ßAßBßC¨fad fa-repeat-alt¨ßEÊßF¨自动驾驶模式¨ßHßIßJËßKËßLËßM¨btn-auto¨´x´Ì´y´ÒßO|÷ßPßQßUÉ}÷¨actions¨|¦¨cmd¨¦¨switch¨‡¨text¨‡´on´¨打开?name¨¨off¨¨关闭?name¨—÷¨triggers¨|{¨source¨ß16¨source_zh¨¨开关状态¨¨state¨|´on´ß19÷¨state_zh¨|´打开´´关闭´÷}÷} 【控制指令与监控数据】 名称 功能按钮/数据接收框的功能 数据键名 指令 WiFi信号 接收WiFi信号数据 num-wifi — 红绿灯数据 接收红绿灯数据(无0,红1,绿2) num-led — 小车偏移角度 接收小车偏移角度 num-angle — 障碍物距离 接收障碍物距离数据 num-distance — 停车 发出停车指令 btn-stoping 0 前进 发出前进指令 btn-go 1 右转 发出右转指令 btn-right 2 左转 发出左转指令 btn-left 3 后退 发出后退指令 btn-back 4 自动驾驶 发出自动驾驶指令 btn-auto 5 Debug 显示收发数据的原始数据格式 — — 【程序流程图】 【主要程序】 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647/***********************************开始***********************************/...int flag= 0; //按键标志位int l= 0; //红绿灯数据int a= 0; //角度数据int d= 0; //距离数据int z= 0; //json解析出来的数据BlinkerNumber Number0("num-wifi");//WIFI信号BlinkerNumber Number1("num-led");//红绿灯信号BlinkerNumber Number2("num-angle");//角度信号BlinkerNumber Number3("num-distance");//距离信号BlinkerButton Button0("btn-stoping");//停止状态按键BlinkerButton Button1("btn-go");//前进状态按键BlinkerButton Button2("btn-right");//右转状态按键BlinkerButton Button3("btn-left");//左转状态按键BlinkerButton Button4("btn-back");//后退状态按键BlinkerButton Button5("btn-auto");//自动驾驶状态按键.../*主循环*/void loop(){ Blinker.run(); Number0.print(WiFi.RSSI()); //发送信号强度 usartEvent();//串口中断 l=int(z/10000); //解析红绿灯数据 a=int((z-10000*l)/100); //解析偏移角度数据d=int(z-10000*l-100*a); //解析距离数据 Number1.print(l); //发送红绿灯信号 Number2.print(a); //发送角度信号 Number3.print(d); //发送距离信号//发送控制指令,灯的亮灭,主要是检查WiFi模块是否接收到数据 if(oState == false && digitalRead(LED_BUILTIN)== LOW)//灯灭 { digitalWrite(LED_BUILTIN,HIGH);//灯灭 Serial.print(flag); //发送指令 } else if(oState == true && digitalRead(LED_BUILTIN)== HIGH)//灯亮 { digitalWrite(LED_BUILTIN,LOW);//灯亮 Serial.print(flag); //发送指令 }}//Blinker初始化略//WiFi连接信号检测略//STM32数据上传解析略.../***********************************结束***********************************/ 3、智能小车的无人控制方案实现智能小车在接收到ESP8266的控制指令和OpenMV路况数据,会根据这些指令数据进行小车运动的控制。 【程序流程图】 【PID控制算法】 关于直流电机的PID调节,主要用来实现车道保持功能。通过OpenMV返回的偏转角度,进行实时调节电机PWM输出,使得偏转角度$Y=50$(也就是小车与中线的偏转角为0,由于之前为了传输方便整体加上了50)。故将设定值定为50,通过实时返回的$Y$值与50做差值运算,得到PID的输入偏差,通过位置式PID返回实时的PWM值。关于PID控制算法,之前也有介绍到,这里不再深入赘述。$$PWM=K_P\\theta(t)+K_i\\sum_{t=0}\\theta(t)+K_d[\\theta(t)-\\theta(t-1)]$$其中为$\\theta(t)$是本次OpenMV返回的偏移角度数据$Y$与50的差值,$\\theta(t-1)$为上一个$Y$与50的差值。 【模拟环境】 【主要程序】 OpenMV-autodrivehttps://github.com/kelecn/OpenMV-autodrive","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"OpenMV","slug":"OpenMV","permalink":"https://kelecn.top/tags/OpenMV/"},{"name":"机器视觉","slug":"机器视觉","permalink":"https://kelecn.top/tags/%E6%9C%BA%E5%99%A8%E8%A7%86%E8%A7%89/"},{"name":"ESP8266","slug":"ESP8266","permalink":"https://kelecn.top/tags/ESP8266/"},{"name":"STM32","slug":"STM32","permalink":"https://kelecn.top/tags/STM32/"}]},{"title":"基于ESP8266的局域网图片刷新显示系统","slug":"28、基于ESP8266的局域网图片刷新显示系统","date":"2021-04-29T09:47:47.000Z","updated":"2021-05-03T11:39:50.000Z","comments":true,"path":"posts/44882/","link":"","permalink":"https://kelecn.top/posts/44882/","excerpt":"简介:简单的说就是通过网页将图片上传至NodeMCU(ESP8266)的flash闪存,再将图片数据通过SPI更新至TFT-LCD显示屏进行显示。","text":"简介:简单的说就是通过网页将图片上传至NodeMCU(ESP8266)的flash闪存,再将图片数据通过SPI更新至TFT-LCD显示屏进行显示。 项目地址:ESP8266-refresh-image-displayhttps://github.com/kelecn/ESP8266-refresh-image-display 欢迎各位去点点小星星哦~ 一、硬件系统 硬件主要用到NodeMCU(ESP8266)和1.44寸TFT彩色液晶屏,淘宝都有得卖,也不贵。 硬件 型号 价格 WiFi模块 NodeMCU(ESP8266) 十几到二十不等 显示屏 1.44寸TFT-LCD液晶屏(SKU:MAR1442) 十几到二十不等 排线 母对母 一两块 NodeMCU(ESP8266)IO口介绍: 1.44寸TFT-LCD液晶屏IO口介绍: 标号 PIN ESP8266开发板对应的接线引脚 引脚说明 1 VCC 3V 电源正 2 GND G 电源地 3 GND - 电源地 4 NC - 无定义,保留,不需要接线 5 NC - 无定义,保留,不需要接线 6 LED 3V LCD背光控制信号(如不需要控制,请接3.3V) 7 CLK D5 LCD SPI总线时钟引脚 8 SDI D7 LCD SPI总线数据引脚 9 RS D1 LCD寄存器/数据选择控制引脚 10 RST D2 LCD复位控制引脚 11 CS D8 LCD片选控制引脚 NodeMCU(ESP8266)与1.44TFT-LCD显示屏接线图: 二、软件系统 1、开发环境搭建 NodeMCU硬件通过USB连接电脑,需提前安装好CH340USB串口驱动。 电脑端提前安装好Arduino开发平台。 安装ESP8266开发板:打开Arduino IDE点击菜单栏的【文件】-【首选项】,添加【附加开发板管理器】网站:https://arduino.esp8266.com/stable/package_esp8266com_index.json 。 然后点击【工具】->【开发板】->【开发板管理器】,搜索 esp8266 后安装。 安装第三方显示屏支持库:将文件夹(Adafruit_ST7735_Library、Adafruit-GFX-Library)移动到Arduino安装目录下的libraries文件夹中,重启Arduino IDE,即可,也就是编程环境搭建完毕。 可选择性安装mDNS服务,安装后,可在浏览器输入域名(host.local)实现访问ESP8266的Web页面,若不安装mDNS服务则通过访问ESP8266实际分配的IP地址实现Web访问。 Mac OS:默认自带mDNS Windows:需安装Bonjour Linux:需安装avahi 2、软件程序 软件实现通过网页将图片上传至NodeMCU(ESP8266),并将图片更新至TFT-LCD显示屏。主要的程序流程图如下: 三、效果演示 1、配网 配置WiFi或热点的名称和密码、设定mDNS地址。 2、程序下载 将下载程序到开发版,待ESP8266与电脑连接上同一个WiFi(或者是电脑开的热点),就可以从串口监视器看到IP地址。 若是使用手机热点,也可以通过终端模拟器应用终端输入ip neigh进行查看连接到手机热点的设备IP地址。 3、图片上传 访问该IP地址即可,访问图片上传Web页面,选择图片上传即可。 4、显示效果 四、参考资料 1、ESP8266 TFT(ST7735)彩屏-web刷图 2、ESP8266网络应用5 - 设置mdns","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"ESP8266","slug":"ESP8266","permalink":"https://kelecn.top/tags/ESP8266/"},{"name":"NodeMCU","slug":"NodeMCU","permalink":"https://kelecn.top/tags/NodeMCU/"},{"name":"Web","slug":"Web","permalink":"https://kelecn.top/tags/Web/"}]},{"title":"内置壁纸系列:持续更新中...","slug":"27、内置壁纸系列:持续更新中...","date":"2021-02-25T09:43:43.000Z","updated":"2021-04-27T08:57:18.000Z","comments":true,"path":"posts/21388/","link":"","permalink":"https://kelecn.top/posts/21388/","excerpt":"简介:本系列主要分享各种内置壁纸,包括但不限于:苹果(Apple)、安卓(Android)、谷歌(Google)、三星(Samsung)、华为(Huawei)、小米(Xiaomi)、OPPO、VIVO、一加(OnePlus)、魅族(Meizu)、联想(Lenovo)、中兴(ZTE)、摩托罗拉(Motorola)、索尼(Sony)、诺基亚(Nokia)、美图(Meitu)、LG、HTC、Elephone等等……","text":"简介:本系列主要分享各种内置壁纸,包括但不限于:苹果(Apple)、安卓(Android)、谷歌(Google)、三星(Samsung)、华为(Huawei)、小米(Xiaomi)、OPPO、VIVO、一加(OnePlus)、魅族(Meizu)、联想(Lenovo)、中兴(ZTE)、摩托罗拉(Motorola)、索尼(Sony)、诺基亚(Nokia)、美图(Meitu)、LG、HTC、Elephone等等…… 愿你美化半生,归来仍是内置壁纸! 项目地址:Built-in-wallpaperhttps://github.com/kelecn/Built-in-wallpaper 喜欢内置壁纸的小伙伴,欢迎去点点小星星哦~ DONE 苹果 谷歌 安卓 华为 小米 Samsung OPPO VIVO 一加 魅族 索尼 LG HTC Lenovo ZTE Motorola Nokia Meitu TODO 其他(Other) 现在基本所有品牌的内置壁纸均已更新到本博客,后续其他新产品发布后,就会同步发布到Github和本博客上,敬请期待。^_^","categories":[{"name":"美化","slug":"美化","permalink":"https://kelecn.top/categories/%E7%BE%8E%E5%8C%96/"}],"tags":[{"name":"内置壁纸","slug":"内置壁纸","permalink":"https://kelecn.top/tags/%E5%86%85%E7%BD%AE%E5%A3%81%E7%BA%B8/"},{"name":"Wallpaper","slug":"Wallpaper","permalink":"https://kelecn.top/tags/Wallpaper/"},{"name":"Google","slug":"Google","permalink":"https://kelecn.top/tags/Google/"},{"name":"数码","slug":"数码","permalink":"https://kelecn.top/tags/%E6%95%B0%E7%A0%81/"}]},{"title":"给博客添加捐赠小部件—sponsor-page","slug":"26、给博客添加捐赠小部件—sponsor-page","date":"2021-01-20T09:43:43.000Z","updated":"2021-08-12T14:55:38.514Z","comments":true,"path":"posts/34795/","link":"","permalink":"https://kelecn.top/posts/34795/","excerpt":"简介:sponsor-page是一个开源的捐赠按钮样式,可以用于美化我们的博客。","text":"简介:sponsor-page是一个开源的捐赠按钮样式,可以用于美化我们的博客。 说在前面,捐赠是不可能捐赠的 只有白嫖才能维持得了生活,捐赠按钮主要还是起一个美化的效果。 参考项目:sponsor-pagehttps://github.com/Kaiyuan/sponsor-page 该项目主要主要有四种捐赠渠道: PayPal 比特币 支付宝 微信支付 我考虑到,比特币在国内使用有限,就把比特币换成了QQ支付,有需要的朋友也可以直接用我的。 sponsor-pagehttps://github.com/kelecn/sponsor-page 一、捐赠按钮的修改 首先,直接把对应项目fork到自己的Github,需要比特币按钮可以选择原版的sponsor-page,需要QQ支付按钮可以选择我修改后的sponsor-page,如果有其他支付方式需求,自己按需修改即可。 第二步,二维码美化,相信大家的路子肯定很多,欢迎在评论区推荐一下,我这边就给大家推荐一个常用的:草料二维码https://cli.im/ 还有PayPal链接、支付宝链接,各位去对应平台获取即可。 第三步,当然就是修改自己对应的支付二维码图片(sponsor-page\\simple\\images)、PayPal(sponsor-page\\simple\\index.html和sponsor-page\\drinks\\index.html)链接和支付宝(sponsor-page\\drinks\\script.js)链接啦,你可以直接在Github上传和修改相应文件即可,也可以克隆到本地,修改后再一起推送到Github。 二、捐赠按钮的使用(部署到Github) 直接开启对应项目的GitHub Pages即可,访问。(博客要是Github+Coding双线部署可能会出现套娃现象,国内线路访问捐赠页面,不会去访问Github Pages,会访问国内的404页面) 下面会介绍对应的解决方法——将捐赠按钮页面部署到博客本地(无论是国内还是国外访问都会正常) 三、捐赠按钮的使用(部署到博客) 首先,定位Volantis主题的捐赠布局。(themes\\volantis\\layout\\_partial\\article.ejs) 修改article.ejs文件。 123456</blockquote></div>//上面用于定位,下面为新添加的,对应修改src即可,要是已经部署到Github Pages,直接使用对应链接即可,要是想放在博客项目中,可往下看<div class='donate'><iframe src="https://kelecn.top/donate/drinks" style="overflow-x:hidden;overflow-y:hidden; border:0xp none #fff; min-height:240px; width:100%;" frameborder="0" scrolling="no"></iframe></div> 部署到博客项目中(本地) 直接将刚才修改好的Github项目克隆到本地,放到博客项目的source中,文件夹命名随意(一般为donate,不要改成sponsor-page以防与前面的开启Github Pages后发生冲突) 在_config.yml中修改 123# Directory...skip_render: "donate/**" #hexo会跳过donate文件夹编译,直接把donate文件夹搬移到public 则对应的捐赠按钮访问链接为 1234//1、域名/donate/drinkshttps://kelecn.top/donate/drinks//2、域名/donate/simplehttps://kelecn.top/donate/simple 捐赠按钮展示(可以用任意Html页面展示) simple页面 1234//simple对应引用代码 </div> <iframe src="https://kelecn.top/donate/simple/" style="overflow-x:hidden;overflow-y:hidden; border:0xp none #fff; min-height:240px; width:100%;" frameborder="0" scrolling="no"></iframe> </div> drinks页面 1234//drinks对应引用代码 </div> <iframe src="https://kelecn.top/donate/drinks/" style="overflow-x:hidden;overflow-y:hidden; border:0xp none #fff; min-height:240px; width:100%;" frameborder="0" scrolling="no"></iframe> </div> 四、特别感谢 特别感谢sponsor-page项目的大力支持,喜欢的朋友,欢迎去其Github点点小星星~ sponsor-pagehttps://github.com/Kaiyuan/sponsor-page","categories":[{"name":"美化","slug":"美化","permalink":"https://kelecn.top/categories/%E7%BE%8E%E5%8C%96/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"Github","slug":"Github","permalink":"https://kelecn.top/tags/Github/"},{"name":"Volantis","slug":"Volantis","permalink":"https://kelecn.top/tags/Volantis/"},{"name":"捐赠","slug":"捐赠","permalink":"https://kelecn.top/tags/%E6%8D%90%E8%B5%A0/"}]},{"title":"jsDelivr CDN的使用及缓存刷新","slug":"25、jsDelivr CDN的使用及缓存刷新","date":"2021-01-08T09:43:43.000Z","updated":"2021-04-03T06:55:24.000Z","comments":true,"path":"posts/22877/","link":"","permalink":"https://kelecn.top/posts/22877/","excerpt":"简介:jsDelivr 是一个免费、开源的加速CDN公共服务。","text":"简介:jsDelivr 是一个免费、开源的加速CDN公共服务。 之前搭建的基于PicGo的GitHub图床,自动生成的链接由于没有经过CDN加速,相当于直接访问Github,常常会出现无法访问的现象,使用体验很一般,现在结合免费的jsDelivr CDN使用效果真不错! 参考项目:基于PicGo的GitHub图床https://kelecn.top/posts/10250/ 一、jsDelivr的使用 首先,jsDelivr的使用[官网](https://www.jsdelivr.com/features)有很详细的介绍,包括npm、Github、Wordpress,下面主要介绍,我通常使用的Github。 也就是说,你要引用一个Github文件,它的链接可以是: 1https://cdn.jsdelivr.net/gh/user/repo@version/file 换成中文也就是: 1https://cdn.jsdelivr.net/gh/您的Github用户名/项目仓库名@项目版本/文件路径 当然了,有些朋友的项目可能没有设置版本,那么可以写成: 1https://cdn.jsdelivr.net/gh/您的Github用户名/项目仓库名@master/文件路径 值得注意的是,文件命名请尽量不要用中文,也不要有空格,这样可以解决大部分无法访问的问题。 二、jsDelivr的缓存刷新 jsDelivr对于我们这些白嫖怪来说,当然是体验很好啦,可惜还是存在一点点小毛病,比如说,CDN缓存刷新不及时,Github那边我已经修改文件好久了,通过CDN访问还是上一个版本,这就很难受。下面介绍一下如何正确刷新jsDelivr的缓存: 官方工具 jsDelivr官方说,不久的将来将会推出用于清除CDN缓存的工具,各位可以期待一下。QAQ 简单方法 对于 jsDelivr,缓存刷新的方式其实很简单,只需将想刷新的链接的开头的cdn 更改为 purge,访问这个接口,返回status: ok,就代表缓存刷新了。 一个例子 12//刷新前https://cdn.jsdelivr.net/gh/kelecn/images@master/myblog.png 12//仅用于刷新返回status: ok,即可https://purge.jsdelivr.net/gh/kelecn/images@master/myblog.png 12//重新访问即可https://cdn.jsdelivr.net/gh/kelecn/images@master/myblog.png 三、特别感谢 特别感谢jsDelivr项目的大力支持,喜欢的朋友,欢迎去其Github点点小星星~ jsDelivrhttps://github.com/jsdelivr/jsdelivr","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://kelecn.top/tags/Github/"},{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://kelecn.top/tags/jsDelivr/"},{"name":"CDN","slug":"CDN","permalink":"https://kelecn.top/tags/CDN/"},{"name":"缓存","slug":"缓存","permalink":"https://kelecn.top/tags/%E7%BC%93%E5%AD%98/"}]},{"title":"初探机器视觉模块OpenMV","slug":"24、初探机器视觉模块OpenMV","date":"2020-12-24T16:50:41.000Z","updated":"2021-04-03T06:55:12.000Z","comments":true,"path":"posts/9237/","link":"","permalink":"https://kelecn.top/posts/9237/","excerpt":"简介:OpenMV是一款开源,低成本,可扩展,支持Python的机器视觉模块,可媲美机器视觉世界的Arduino。","text":"简介:OpenMV是一款开源,低成本,可扩展,支持Python的机器视觉模块,可媲美机器视觉世界的Arduino。 OpenMV官方教程: OpenMV中文教程https://book.openmv.cc/ 开源项目GitHub链接: OpenMVhttps://github.com/openmv 一、OpenMV简介 OpenMV是一个开源,低成本,功能强大的机器视觉模块。以STM32F427CPU为核心,集成了OV7725摄像头芯片,在小巧的硬件模块上,用C语言高效地实现了核心机器视觉算法,并提供Python编程接口。 OpenMV采用的STM32F427拥有丰富的硬件资源,引出UART,I2C,SPI,PWM,ADC,DAC以及GPIO等接口方便扩展外围功能。USB接口用于连接电脑上的集成开发环境OpenMVIDE,协助完成编程、调试和更新固件等工作。TF卡槽支持大容量的TF卡(最大32GB),可以用于存放程序和保存照片等。 Your browser does not support the video tag. 二、OpenMV功能 OpenMV上的机器视觉算法包括寻找色块、人脸检测、眼球跟踪、边缘检测、标志跟踪等。可以用来实现非法入侵检测、产品的残次品筛选、跟踪固定的标记物等。使用者仅需要写一些简单的Python代码,即可轻松的完成各种机器视觉相关的任务。小巧的设计,使得OpenMV可以用到很多创意的产品上。比如,可以给自己的机器人提供周边环境感知能力;给智能车增加视觉巡线功能;给智能玩具增加识别人脸功能,提高产品趣味性等;甚至,可以给工厂产品线增加残次品筛选功能等。 OpenMV的定位是“带机器视觉功能的“Arduino”。它可以通过UART,I2C,SPI,AsyncSerial以及GPIO等控制其他的硬件,甚至是单片机模块,如Arduino、RaspberryPi(树莓派)等。它也可以被其他的单片机模块控制。这个特点使得它可以很灵活的和其他流行的模块配合,实现复杂的产品功能。 三、OpenMV项目 追球小车 巡线小车 口罩检测 人脸识别 垃圾分类","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"硬件","slug":"硬件","permalink":"https://kelecn.top/tags/%E7%A1%AC%E4%BB%B6/"},{"name":"OpenMV","slug":"OpenMV","permalink":"https://kelecn.top/tags/OpenMV/"},{"name":"机器视觉","slug":"机器视觉","permalink":"https://kelecn.top/tags/%E6%9C%BA%E5%99%A8%E8%A7%86%E8%A7%89/"},{"name":"Python","slug":"Python","permalink":"https://kelecn.top/tags/Python/"}]},{"title":"基于QT开发的局域网聊天室","slug":"23、基于QT开发的局域网聊天室","date":"2020-11-11T12:00:01.000Z","updated":"2021-04-03T06:55:02.000Z","comments":true,"path":"posts/29647/","link":"","permalink":"https://kelecn.top/posts/29647/","excerpt":"简介:基于QT开发的一个简易的局域网聊天室。","text":"简介:基于QT开发的一个简易的局域网聊天室。 注意事项: 12345开发工具:QT开发语言:C++测试软件:LAN-Chat-Room-Test文件夹程序源码:LAN-Chat-Room-Code文件夹注意:Windows环境下请在全英文路径下打开工程,否则会因为编码出错。 GitHub链接: LAN-Chat-Roomhttps://github.com/kelecn/LAN-Chat-Room 一、功能简介 本次设计是一个简易的局域网聊天室,功能设计主要分为群聊和私聊两部分,每部分都支持基础聊天以及文件传输功能。参考了《Qt及Qt Quick开发实战精解》中的群聊实例,并在群聊的基础设计了私聊这部分内容以及其他一些功能,其中消息传递使用UDP来实现,而文件传输使用TCP来实现。 二、UDP群聊部分 本程序实现的功能是:局域网内,每个用户登录到聊天软件,则软件界面的右端可以显示在线用户列表,分别显示的是用户名,主机名,ip地址。软件左边那大块是聊天内容显示界面,这里局域网相当于qq中的qq群,即群聊。每个人可以在聊天输入界面中输入文字(还可修改文字格式&颜色)并发送。其聊天界面如下: 注:此处的客户端,同时也是服务器。 下面分服务器端和客户端两部分来介绍: 服务器: 建立一个UDP Socket并绑定在固定端口后,用信号与槽的方式进行监听是否有数据来临。如果用,接收其数据并分析数据的消息类型,如果消息是新用户登录则更新用户列表并在聊天显示窗口中添加新用户上线通知;同理,如果是用户下线,则在用户列表中删除该用户且在聊天显示窗口中显示下线通知;如果是聊天消息,则接收该消息并且在窗口中显示。其流程图如下: 客户端: 首先当客户端登录时,获取本机的用户名,计算机名和ip地址,并广播给局域网的服务器更新用户列表。然后当客户端需要发送信息时,则在聊天输入栏中输入信息并按发送键发送聊天内容,当然于此同时也广播本地系统的各种信息。其流程图如下: 三、TCP文件传输部分 发送端界面: 接收端界面: 发送端,也即承担服务器角色的操作: 在主界面程序右侧选择一个需要发送文件的用户,弹出发送端界面后,点击打开按钮,在本地计算机中选择需要发送的文件,点击发送按钮,则进度条上会显示当前文件传送的信息,有已传送文件大小信息,传送速度等信息。如果想关闭发送过程,则单击关闭按钮。其流程图如下: 接收端,也即承担客户端角色的操作: 当在主界面中突然弹出一个对话框,问是否接自某个用户名和IP地址的文件传送信息,如果接受则单击yes按钮,否则就单击no按钮。当接收文件时,选择好接收文件所存目录和文件名后就开始接收文件了,其过程也会显示已接收文件的大小,接收速度和剩余时间的大小等信息。其流程图如下: 四、私聊部分 私聊界面: 私聊发送端流程图: 私聊接收端流程图: 下面来介绍下2者实现的具体过程: A方(主动开始首次发送的一方): 在主窗口右侧双击自己想与之聊天的B方,此时A方实际上完成的工作有:用B方的主机名和ip地址新建了私聊的类privatechat,在新建该类的过程中,已经设置了显示顶端为:xxx与聊天中,对方IP:xxx,且绑定了本地ip和私聊的专用端口,同时设置了信号与槽的联系,即该端口如果有数据输入,则触发槽函数processPendingDatagrams().该函数是char.cpp中的。 当上面的新建私聊类完成后,用通讯对方ip地址和其群聊专用的端口(但用的是主udp群聊的socket进行的)将以下内容分别发送出去:消息类型(Xchat),用户名,主机名,本地ip地址。完成后,在屏幕中显示私聊窗口。 在私聊窗口中输入需要聊天的内容,单击发送键。该过程玩成的内容有:分别将消息类型(Message)+用户名+本地名+本地IP+消息内容本身通过私聊专用端口发送出去。在私聊窗口中显示主机名+聊天时间,换行后显示消息内容本身。 B方(第一次信息是他人发送过来的): 当A在2步骤中用群聊的方法发送其消息类型(Xchat),其用户名,其主机名,其ip地址后,由于程序运行时已经初始化了widget.cpp中的构造函数,所以每个程序都绑定了本地地址+群聊专用的端口,一旦有数据传入,就触发widget.cpp中的槽函数processPendingDatagrams(). 在processPendingDatagrams()函数中,判断消息类型为Xchat后,接收缓存区内接收对方用户名,对方主机名和对方ip地址。并用接收到的主机名和ip地址新建一个私聊类。新建该私聊的过程与A中的步骤1一样。完后在程序中显示私聊窗口。 当对方A按完发送按钮后,通过私聊专用端口绑定槽函数来触发chart.cpp中的processPendingDatagrams()函数,该函数中先读取消息类型(Message),然后依次读取用户名,主机名,ip地址,消息内容本身,并将对方信息和消息内容显示在聊天窗口中。 五、参考资料及资源下载 《Qt及Qt Quick开发实战精解》","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"TCP","slug":"TCP","permalink":"https://kelecn.top/tags/TCP/"},{"name":"UDP","slug":"UDP","permalink":"https://kelecn.top/tags/UDP/"},{"name":"QT","slug":"QT","permalink":"https://kelecn.top/tags/QT/"}]},{"title":"PID控制算法","slug":"22、PID控制算法","date":"2020-09-09T12:00:00.000Z","updated":"2021-04-03T06:54:52.000Z","comments":true,"path":"posts/16358/","link":"","permalink":"https://kelecn.top/posts/16358/","excerpt":"简介:PID控制算法是结合比例、积分和微分三种环节于一体的控制算法,它是连续系统中技术最为成熟、应用最为广泛的一种控制算法。","text":"简介:PID控制算法是结合比例、积分和微分三种环节于一体的控制算法,它是连续系统中技术最为成熟、应用最为广泛的一种控制算法。 一、什么是PID算法 PID,就是“比例(proportional)、积分(integral)、微分(derivative)”,是一种很常见的控制算法。常用于需要将某一个物理量“保持稳定”的场合(比如维持平衡,稳定温度、转速等)。 总的来说,当得到系统的输出后,将输出经过比例,积分,微分3种运算方式,叠加到输入中,从而控制系统的行为。 在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法,如果能够熟练掌握PID算法的设计与实现过程,对于一般的研发人员来讲,应该是足够应对一般研发问题了,而难能可贵的是,在我所接触的控制算法当中,PID控制算法又是最简单,最能体现反馈思想的控制算法,可谓经典中的经典。经典的未必是复杂的,经典的东西常常是简单的,而且是最简单的,想想牛顿的力学三大定律吧,想想爱因斯坦的质能方程吧,何等的简单!简单的不是原始的,简单的也不是落后的,简单到了美的程度。 Your browser does not support the video tag. 二、PID算法原理 $$U(t)=K_P[e(t)+\\frac{1}{T_I}\\int{e(t)}dt+\\frac{T_De(t)}{dt}]$$ 三个重要的参数: Kp:控制器的比例系数。 Ti:控制器的积分时间,也称积分系数。 Td:控制器的微分时间,也称微分系数。 1、P - 比例部分 比例环节的作用是对偏差瞬间作出反应。偏差一旦产生控制器立即产生控制作用, 使控制量向减少偏差的方向变化。 控制作用的强弱取决于比例系数Kp, 比例系数Kp越大,控制作用越强, 则过渡过程越快, 控制过程的静态偏差也就越小; 但是Kp越大,也越容易产生振荡, 破坏系统的稳定性。 故而,比例系数Kp选择必须恰当, 才能过渡时间少, 静差小而又稳定的效果。 2、I - 积分部分 从积分部分的数学表达式可以知道, 只要存在偏差, 则它的控制作用就不断的增加; 只有在偏差e(t)=0时, 它的积分才能是一个常数,控制作用才是一个不会增加的常数。 可见,积分部分可以消除系统的偏差。 我们需要设置一个积分量。只要偏差存在,就不断地对偏差进行积分(累加),并反应在调节力度上。 积分环节的调节作用虽然会消除静态误差,但也会降低系统的响应速度,增加系统的超调量。积分常数Ti越大,积分的积累作用越弱,这时系统在过渡时不会产生振荡; 但是增大积分常数Ti会减慢静态误差的消除过程,消除偏差所需的时间也较长, 但可以减少超调量,提高系统的稳定性。 当 Ti 较小时, 则积分的作用较强,这时系统过渡时间中有可能产生振荡,不过消除偏差所需的时间较短。所以必须根据实际控制的具体要求来确定Ti 。 3、D - 微分部分 实际的控制系统除了希望消除静态误差外,还要求加快调节过程。在偏差出现的瞬间,或在偏差变化的瞬间, 不但要对偏差量做出立即响应(比例环节的作用), 而且要根据偏差的变化趋势预先给出适当的纠正。为了实现这一作用,可在 PI 控制器的基础上加入微分环节,形成 PID 控制器。 我们需要一个控制作用,让被控制的物理量的“变化速度”趋于0,即类似于“阻尼”的作用。 微分环节的作用使阻止偏差的变化。它是根据偏差的变化趋势(变化速度)进行控制。偏差变化的越快,微分控制器的输出就越大,并能在偏差值变大之前进行修正。微分作用的引入, 将有助于减小超调量, 克服振荡, 使系统趋于稳定, 特别对髙阶系统非常有利, 它加快了系统的跟踪速度。但微分的作用对输入信号的噪声很敏感,对那些噪声较大的系统一般不用微分, 或在微分起作用之前先对输入信号进行滤波。 三、PID算法应用实例 PID 控制算法可以分为位置式 PID 和增量式 PID 控制算法。 两者的区别: (1)位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大; (2)增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。 (3)由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。 下面给出公式直接体现的C语言源代码(请结合项目修改源代码): 1.位置式PID $$u(k)=K_Pe(k)+K_I\\sum_{i=0}{e(i)}+K_D[e(k)-e(k-1)]$$ 1234567891011121314151617181920212223242526272829303132typedef struct{ float Kp; //比例系数Proportional float Ki; //积分系数Integral float Kd; //微分系数Derivative float Ek; //当前误差 float Ek1; //前一次误差 e(k-1) float Ek2; //再前一次误差 e(k-2) float LocSum; //累计积分位置}PID_LocTypeDef;/************************************************函数名称 : PID_Loc功 能 : PID位置(Location)计算参 数 : SetValue ------ 设置值(期望值) ActualValue --- 实际值(反馈值) PID ----------- PID数据结构返 回 值 : PIDLoc -------- PID位置作 者 : strongerHuang*************************************************/float PID_Loc(float SetValue, float ActualValue, PID_LocTypeDef *PID){ float PIDLoc; //位置 PID->Ek = SetValue - ActualValue; PID->LocSum += PID->Ek; //累计误差 PIDLoc = PID->Kp * PID->Ek + (PID->Ki * PID->LocSum) + PID->Kd * (PID->Ek1 - PID->Ek); PID->Ek1 = PID->Ek; return PIDLoc;} 2.增量式PID $$\\Delta u(k)=u(k)-u(k-1)=K_P[e(k)-e(k-1)]+K_Ie(k)+K_D[e(k)-2e(k-1)-e(k-2)]$$ 123456789101112131415161718192021222324252627282930typedef struct{ float Kp; //比例系数Proportional float Ki; //积分系数Integral float Kd; //微分系数Derivative float Ek; //当前误差 float Ek1; //前一次误差 e(k-1) float Ek2; //再前一次误差 e(k-2)}PID_IncTypeDef;/************************************************函数名称 : PID_Inc功 能 : PID增量(Increment)计算参 数 : SetValue ------ 设置值(期望值) ActualValue --- 实际值(反馈值) PID ----------- PID数据结构返 回 值 : PIDInc -------- 本次PID增量(+/-)作 者 : strongerHuang*************************************************/float PID_Inc(float SetValue, float ActualValue, PID_IncTypeDef *PID){ float PIDInc; //增量 PID->Ek = SetValue - ActualValue; PIDInc = (PID->Kp * PID->Ek) - (PID->Ki * PID->Ek1) + (PID->Kd * PID->Ek2); PID->Ek2 = PID->Ek1; PID->Ek1 = PID->Ek; return PIDInc;} 四、参考文章 一文读懂PID控制算法(抛弃公式,从原理上真正理解PID控制) 重温经典PID算法 PID原理和参数调试 结合案例轻松理解PID到底是个啥? 视频来源:【编程三分钟】通俗易懂的PID控制算法讲解 特别鸣谢:微信公众号:strongerHuang的知识整理","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"C语言","slug":"C语言","permalink":"https://kelecn.top/tags/C%E8%AF%AD%E8%A8%80/"},{"name":"PID","slug":"PID","permalink":"https://kelecn.top/tags/PID/"},{"name":"算法","slug":"算法","permalink":"https://kelecn.top/tags/%E7%AE%97%E6%B3%95/"}]},{"title":"C语言内存管理","slug":"21、C语言内存管理","date":"2020-09-07T12:00:00.000Z","updated":"2021-04-03T06:54:44.000Z","comments":true,"path":"posts/24097/","link":"","permalink":"https://kelecn.top/posts/24097/","excerpt":"简介:嵌入式系统的内存资源很稀缺,其内存页会更小,在嵌入式开发当中需要特别注意内存管理。","text":"简介:嵌入式系统的内存资源很稀缺,其内存页会更小,在嵌入式开发当中需要特别注意内存管理。 一、内存分配方式 内存分配方式有三种: [1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 [2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 [3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释 放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大 小的堆空间将会产生堆内碎块。 二、程序的内存空间 在C语言程序中,代码是放在内存中执行的,我们大致将程序所占用的内存分为四个区域:栈区 堆区 数据区 代码区。 一个由C/C++编译的程序占用的内存分为以下几个部分, 1、栈区(stack) 由编译器自动分配释放 ,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。 例如: 123void recive(int a,int b) { int c;}//其中,参数a,参数b和变量c都是存放在栈区,当函数执行完毕的时候,它们占有的空间自动释放。 2、堆区(heap) 一般由程序员分配释放(malloc&free), 若程序员不释放,程序结束时可能由OS(operating system)回收 。分配方式类似于链表。例如: 123char *src;src = (char*)malloc(4 *sizeof(char));//动态分配内存,表示查找可用的连续4个字节内存的空间,并将该内存首地址强制转换为指向字符数据的指针赋给scr,为src这个指针变量分配4个char类型的空间。 3、数据区(全局区) 1、(文字)常量区:存放常量,一般是字符串常量。2、静态区(static):存放全局变量和静态变量。该区域是在程序结束后由操作系统释放。 4、程序代码区 存放函数体(类成员函数和全局函数)的二进制代码,也是由操作系统进行管理。 三、一个例子 12345678910111213141516171819202122232425262728#include <stdio.h>#include <stdlib.h>int main(){ int i; char* p1[3]= {"abc","def","ghi"}; char p2[3][4]= {"123","456","789"}; char** p3= (char**)malloc( 3 * sizeof(char*)); for(i=0;i < 3;i++) { p3[i]= (char *)malloc ( 5 * sizeof(char)); sprintf(p3[i],"%d%d%d",i,i,i); /* sprintf()函数:最常见的应用之一是把整数打印到字符串中 例如: srpintf(s,"%d",123); 输出为:"123" 输出结果不会打印在屏幕上,而是写入字符串中 */ printf("%s\\n",p3[i]); /* printf()函数才将结果输出到屏幕上 */ } free(p3); return 0;} 四、参考资料 C语言知识整理(3):内存管理(详细版) C语言中一些关于内存四区的归纳","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"C语言","slug":"C语言","permalink":"https://kelecn.top/tags/C%E8%AF%AD%E8%A8%80/"},{"name":"内存","slug":"内存","permalink":"https://kelecn.top/tags/%E5%86%85%E5%AD%98/"}]},{"title":"常见总线通讯协议:SPI、I2C、UART、USART、CAN、USB等","slug":"20、常见总线通讯协议:SPI、I2C、UART、USART、CAN、USB","date":"2020-09-05T13:50:00.000Z","updated":"2021-10-22T11:54:29.030Z","comments":true,"path":"posts/55976/","link":"","permalink":"https://kelecn.top/posts/55976/","excerpt":"简介:介绍常见总线通讯协议:SPI、I2C、UART、USART、CAN、USB等。","text":"简介:介绍常见总线通讯协议:SPI、I2C、UART、USART、CAN、USB等。 一、SPI:串行外设接口 SPI(Serial Peripheral Interface)是一种由Motorola公司提出的串行同步通讯协议,由一个主设备和一个或多个从设备组成。其拥有四根(类)硬脚引线,分别为 SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟),CS(片选)。因为一个主设备可以挂多个从设备,则通过片选引脚对从设备进行选择。从设备的工作时钟则是来自于主设备的SCK线。 1.1 电路示意图 1.2 数据的传输 SPI在数据传输的时候,需要确定两件事情:其一,数据是在时钟的上升沿采集还是下降沿采集;其二,时钟的初始(空闲)状态是为高电平还是低电平。而I2C的空闲状态,时钟线为高电平;数据采集的时候,时钟线也为高电平。但SPI给出了更自由的方式。 CPOL:时钟极性, 表示 SPI 在空闲时, 时钟信号是高电平还是低电平。 CPHA:时钟相位, 表示 SPI 设备是在 SCK 管脚上的时钟信号变为上升沿时触发数据采样, 还是在时钟信号变为下降沿时触发数据采样。 那么,SPI CPOL有两种可能,CPHA有两种可能,则SPI数据传输就有四种可能—按照标准的说法,SPI数据传输就有四种模式。 1.3 SPI读写 SPI在硬件设计上采用的双数据线制,根据设计,在SPI通信过程中,主从设备之间会形成一个数据环形链路—也即是,主设备向从设备写一次数据,从设备就会回一次数据(至于该从设备回复的数据是否有效,则另当别论—如果有效,主设备就把它读入;如果无效,则丢弃即可)。 二、I2C:意为IC之间总线 I²C (Inter-Integrated Circuit)。其拥有一根数据线SDA和一根时钟线SCL。其总线通过上拉电阻与电源相连接。每个接到I2C总线上的器件都有唯一的地址。其中,主动发起操作的一方为主机,另外一方为从机。 1.1 电路示意图 1.2 数据传输 当没有数据传输的时候,两根总线都为高电平;当采集IIC上的数据时,其时钟线SCL必须是高电平且SDA的数据必须保持稳定不变—将SDA的电平与SCL的高电平进行“与”操作后,以便确定SDA上是1还是0;在SCL为低电平的时候,SDA上的数据可以进行跳变。 数据传输开始时,需要发送一个起始信号;数据传输结束后,需要发送一个终止信号;每8bit数据传输结束,都需要一个ACK。起止信号都有Master发出,而ACK则可能由Master或者SLAVE来发出。数据的传输采用大端传输。 开始信号:SCL为高电平,SDA的电平由高跳到低表示开始信号。 终止信号:SCL为高电平,SDA的电平由低跳到高表示终止信号。 1.3 数据协议 1.4 I2C读写流程 三、UART:通用异步收发器 1.1 电路示意图 UART是一个大家族,其包括了RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。它们的主要区别在于其各自的电平范围不相同。 嵌入式设备中常常使用到的是TTL、TTL转RS232的这种方式。常用的就三根引线:发送线TX、接收线RX、电平参考地线GND。 1.2 通信协议 将传输数据的每个字符一位接一位地传输。 起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。 数据位:紧接着起始位之后。数据位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。 奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。 停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。 波特率:数据传输的速率。有以下几个档位:300、600、1200、2400、4800、9600、19200、38400、43000、56000、57600、115200.当然也可以自定义。在数据传输和接收双方,需要预先统一波特率,以便正确的传输数据。 四、USART:通用同步异步收发器 USART是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。 12345678910111. 全双工操作(相互独立的接收数据和发送数据);2. 同步操作时,可主机时钟同步,也可从机时钟同步;3. 独立的高精度波特率发生器,不占用定时/计数器;4. 支持5、6、7、8和9位数据位,1或2位停止位的串行数据桢结构;5. 由硬件支持的奇偶校验位发生和检验;6. 数据溢出检测;7. 帧错误检测;8. 包括错误起始位的检测噪声滤波器和数字低通滤波器;9. 三个完全独立的中断,TX发送完成、TX发送数据寄存器空、RX接收完成;10.支持多机通信模式;11.支持倍速异步通信模式。 五、CAN:现场总线 CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。 12345CAN属于现场总线的范畴,它是一种有效支持分布式控制或实时控制的串行通信网络。较之许多RS-485基于R线构建的分布式控制系统而言,基于CAN总线的分布式控制系统在以下方面具有明显的优越性:1、网络各节点之间的数据通信实时性强2、开发周期短3、已形成国际标准的现场总线4、最有前途的现场总线之一 六、LIN:局域互联网络 LIN总线是针对汽车分布式电子系统而定义的一种低成本的串行通讯网络,是对控制器区域网络(CAN)等其它汽车多路网络的一种补充,适用于对网络的带宽、性能或容错功能没有过高要求的应用。LIN总线是基于SCI(UART)数据格式,采用单主控制器/多从设备的模式,是UART中的一种特殊情况。 七、USB:通用串行总线 USB是英文Universal Serial BUS(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯,是应用在PC 领域的接口技术。USB 接口支持设备的即 即用和热插拔功能。USB 是在1994 年底由尔、康柏.IBM、Microsoft 等多家公司联合提出的.USB的电气特性还有传输特性 八、GPIO:通用输入/输出 GPIO(General Purpose Input Output )为通用输入/输出,通用端口,总线扩展器, 利用工业标准I2C、SMBus™或SPI™接口简化了I/O口的扩展。当微控制器或芯片组没有足够的I/O端口,或当系统需要采用远端串行通信或控制时,GPIO产品能够提供额外的控制和监视功能。 由于LIN网络在汽车中一般不独立存在,通常会与上层CAN网络相连,形成CAN-LIN网关节点。 九、SPI、I2C、UART、CAN、LIN对比 十、参考文献 通信方式梳理:GPIO,I2C,SPI,UART,USART,USB的区别 通信总线协议学习整理 通信方式梳理:GPIO,I2C,SPI,UART,USART,USB的区别 SPI、I2C、UART、CAN","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"SPI","slug":"SPI","permalink":"https://kelecn.top/tags/SPI/"},{"name":"I2C","slug":"I2C","permalink":"https://kelecn.top/tags/I2C/"},{"name":"USB","slug":"USB","permalink":"https://kelecn.top/tags/USB/"},{"name":"总线","slug":"总线","permalink":"https://kelecn.top/tags/%E6%80%BB%E7%BA%BF/"}]},{"title":"精简指令集计算机(RISC)和复杂指令集计算机(CISC)的区别","slug":"19、复杂指令集计算机(CISC)和精简指令集计算机(RISC)的区别","date":"2020-09-01T13:50:00.000Z","updated":"2021-04-03T06:54:24.000Z","comments":true,"path":"posts/38602/","link":"","permalink":"https://kelecn.top/posts/38602/","excerpt":"简介:精简指令集计算机(RISC)和复杂指令集计算机(CISC)的区别","text":"简介:精简指令集计算机(RISC)和复杂指令集计算机(CISC)的区别 一、RISC(精简指令集计算机) RISC(reduced instruction set computer,精简指令集计算机)是一种执行较少类型计算机指令的微处理器。这样一来,它能够以更快的速度执行操作。因为计算机执行每个指令类型都需要额外的晶体管和电路元件,计算机指令集越大就会使微处理器更复杂,执行操作也会更慢。 二、 CISC(复杂指令集计算机) 除了RISC,任何全指令集计算机都使用的是CISC(complexinstruction set computer,复杂指令集计算机)。 目前常见使用RISC的处理器包括DEC Alpha、ARC、ARM、MIPS、PowerPC、SPARC和SuperH等。 常见使用CISC的处理器主要有X86. 三、RISC和CISC的区别 (1) 指令系统:RISC 设计者把主要精力放在那些经常使用的指令上,尽量使它们具有简单高效的特色。对不常用的功能,常通过组合指令来完成。因此,在RISC 机器上实现特殊功能时,效率可能较低。但可以利用流水技术和超标量技术加以改进和弥补。而CISC 计算机的指令系统比较丰富,有专用指令来完成特定的功能。因此,处理特殊任务效率较高。 (2) 存储器操作:RISC 对存储器操作有限制,使控制简单化;而CISC 机器的存储器操作指令多,操作直接。 (3) 程序:RISC 汇编语言程序一般需要较大的内存空间,实现特殊功能时程序复杂,不易设计;而CISC 汇编语言程序编程相对简单,科学计算及复杂操作的程序设计相对容易,效率较高。 (4) 中断:RISC 机器在一条指令执行的适当地方可以响应中断;而CISC 机器是在一条指令执行结束后响应中断。 (5) CPU芯片电路:RISC CPU 包含有较少的单元电路,因而面积小、功耗低;而CISC CPU 包含有丰富的电路单元,因而功能强、面积大、功耗大。 (6) 设计周期:RISC 微处理器结构简单,布局紧凑,设计周期短,且易于采用最新技术;CISC 微处理器结构复杂,设计周期长。 (7) 用户使用:RISC 微处理器结构简单,指令规整,性能容易把握,易学易用;CISC微处理器结构复杂,功能强大,实现特殊功能容易。 (8) 应用范围:由于RISC 指令系统的确定与特定的应用领域有关,故RISC 机器更适合于专用机;而CISC 机器则更适合于通用机。 四、参考文章 RISC和CISC的区别","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"CISC","slug":"CISC","permalink":"https://kelecn.top/tags/CISC/"},{"name":"RISC","slug":"RISC","permalink":"https://kelecn.top/tags/RISC/"},{"name":"指令集","slug":"指令集","permalink":"https://kelecn.top/tags/%E6%8C%87%E4%BB%A4%E9%9B%86/"}]},{"title":"几种常见的DC-DC拓扑","slug":"18、几种常见的DC-DC拓扑","date":"2020-08-28T13:50:00.000Z","updated":"2021-04-03T06:54:14.000Z","comments":true,"path":"posts/50067/","link":"","permalink":"https://kelecn.top/posts/50067/","excerpt":"简介:开关电源三大基础拓扑解析:BUCK/BOOST/BUCK-BOOST","text":"简介:开关电源三大基础拓扑解析:BUCK/BOOST/BUCK-BOOST 一、Buck 拓扑电路 Buck电路是一个降压电路,Uin=Ul+Uo。因Uin>Uo,故具有降压作用。 二、Boost拓扑电路 Boost电路是一个升压电路,Uo=Uin+Ul-Ud ,由于Ud值较小,忽略不计,Uin+Ul>Uo,故具有升压作用。 三、Buck-Boost拓扑电路 其中的器件和Buck电路完全一致,只是开关SW,二极管和电感的位置发生了改变 Buck-Boost变换器输出的是相对地的负压四、参考文献 开关电源学习笔记1 — Buck变换器的基本原理开关电源学习笔记2 — Boost变换器的基本原理开关电源学习笔记3 — Buck-Boost变换器的基本原理开关电源三大基础拓扑解析:BUCK/BOOST/BUCK-BOOST","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"Boost","slug":"Boost","permalink":"https://kelecn.top/tags/Boost/"},{"name":"Buck","slug":"Buck","permalink":"https://kelecn.top/tags/Buck/"},{"name":"DCDC","slug":"DCDC","permalink":"https://kelecn.top/tags/DCDC/"}]},{"title":"一文搞懂TCP与UDP的区别","slug":"17、一文搞懂TCP与UDP的区别","date":"2020-08-26T13:57:00.000Z","updated":"2021-04-03T06:54:04.000Z","comments":true,"path":"posts/22814/","link":"","permalink":"https://kelecn.top/posts/22814/","excerpt":"简介:网络协议是每个IT工程师都必须要掌握的知识,TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP,本文将介绍下这两者以及它们之间的区别。","text":"简介:网络协议是每个IT工程师都必须要掌握的知识,TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP,本文将介绍下这两者以及它们之间的区别。 一、TCP/IP网络模型 计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议(protocol)。 TCP/IP 是互联网相关的各类协议族的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议。 TCP/IP模型是互联网的基础,它是一系列网络协议的总称。这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。 链路层:负责封装和解封装IP报文,发送和接受ARP/RARP报文等。 网络层:负责路由以及把分组报文发送给目标网络或主机。 传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文。 应用层:负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等。 在网络体系结构中网络通信的建立必须是在通信双方的对等层进行,不能交错。 在整个数据传输过程中,数据在发送端时经过各层时都要附加上相应层的协议头和协议尾(仅数据链路层需要封装协议尾)部分,也就是要对数据进行协议封装,以标识对应层所用的通信协议。接下去介绍TCP/IP 中有两个具有代表性的传输层协议—-TCP 和 UDP。 二、UDP UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。 它有以下几个特点: 1. 面向无连接 首先 UDP 是不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。 具体来说就是: 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作 2. 有单播,多播,广播的功能 UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。 3. UDP是面向报文的 发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。因此,应用程序必须选择合适大小的报文 4. 不可靠性 首先不可靠性体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。 并且收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了。 再者网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP。 从上面的动态图可以得知,UDP只会把想发的数据报文一股脑的丢给对方,并不在意数据有无安全完整到达。 5. 头部开销小,传输数据报文时是很高效的。 UDP 头部包含了以下几个数据: 两个十六位的端口号,分别为源端口(可选字段)和目标端口 整个数据报文的长度 整个数据报文的检验和(IPv4 可选 字段),该字段用于发现头部信息和数据中的错误 因此 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的 三、TCP 当一台计算机想要与另一台计算机通讯时,两台计算机之间的通信需要畅通且可靠,这样才能保证正确收发数据。例如,当你想查看网页或查看电子邮件时,希望完整且按顺序查看网页,而不丢失任何内容。当你下载文件时,希望获得的是完整的文件,而不仅仅是文件的一部分,因为如果数据丢失或乱序,都不是你希望得到的结果,于是就用到了TCP。 TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排水管中的水流。 1. TCP连接过程 如下图所示,可以看到建立一个TCP连接的过程为(三次握手的过程): 第一次握手 客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。 第二次握手 服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。 第三次握手 当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。 这里可能大家会有个疑惑:为什么 TCP 建立连接需要三次握手,而不是两次?这是因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。 2. TCP断开链接 TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。 第一次握手 若客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求。 第二次握手 B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A。 第三次握手 B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入 LAST-ACK 状态。 第四次握手 A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。 3. TCP协议的特点 面向连接 面向连接,是指发送数据之前必须在两端建立连接。建立连接的方法是“三次握手”,这样能建立可靠的连接。建立连接,是为数据的可靠传输打下了基础。 仅支持单播传输 每条TCP传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。 面向字节流 TCP不像UDP一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输。 可靠传输 对于可靠传输,判断丢包,误码靠的是TCP的段编号以及确认号。TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。 提供拥塞控制 当网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞 TCP提供全双工通信 TCP允许通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据。当然,TCP可以立即发送一个数据段,也可以缓存一段时间以便一次发送更多的数据段(最大的数据段大小取决于MSS) 四、TCP和UDP的比较 1. 对比 UDP TCP 是否连接 无连接 面向连接 是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制 连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信 传输方式 面向报文 面向字节流 首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节 适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输 2. 总结 TCP向上层提供面向连接的可靠服务 ,UDP向上层提供无连接不可靠服务。 虽然 UDP 并没有 TCP 传输来的准确,但是也能在很多实时性要求高的地方有所作为 对数据准确性要求高,速度可以相对较慢的,可以选用TCP 五、参考文章 一文搞懂TCP与UDP的区别 TCP的三次握手与四次挥手理解及面试题(很全面)","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"TCP","slug":"TCP","permalink":"https://kelecn.top/tags/TCP/"},{"name":"UDP","slug":"UDP","permalink":"https://kelecn.top/tags/UDP/"},{"name":"计算机网络","slug":"计算机网络","permalink":"https://kelecn.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"}]},{"title":"C语言基础知识(复习)","slug":"16、C语言基础知识(复习)","date":"2020-08-25T13:57:00.000Z","updated":"2021-04-03T06:53:56.000Z","comments":true,"path":"posts/57201/","link":"","permalink":"https://kelecn.top/posts/57201/","excerpt":"简介: 嵌入式笔试相关题目、考点。","text":"简介: 嵌入式笔试相关题目、考点。 一、static关键词 1)、用于声明函数体内的变量为静态局部变量,存储在静态数据存储区,在函数被调用过程中维持其值保持不变。2)、在文件内(函数体外)被声明为静态的变量,可以被文件内的所有函数访问,但不能被其他文件的函数访问,是一个本地的局部变量。3)、在文件内,被声明为静态的函数只可被文件内的其他函数调用,但不能被其他文件的函数调用。 二、const关键词 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。不管出现在任何上下文都是为这个目的而服务的。注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。 123const int a;//同上面的代码行是等价的,都表示一个常整形数。int const a; 12345678910111213/* const具有"左结合"性,即const修饰*,那么,不难理解,该句表示一个指向整数的常指针, a指向的整数可以修改,但指针a不能修改。*/int *const a;/* 同理,下面的这两行,根据"左结合"性,const修饰的是(*a),也即是一个整数, 所以,这两句表示指针指向一个常整数。*/const int *a;int const *a;//根据"左结合"性质,第一个const修饰(*),第二个const修饰(a),因此,这句话表示一个指向常整数的常指针。int const *a const; 123456789101112131415161718192021222324252627282930313233343536373839const char *p; //*p是const,p可变const (char *) p;//p是const,*p可变char* const p; //p是const,*p可变const char* const p; //p和*p都是constchar const * p;// *p是const,p可变(char*) const p;//p是const,*p可变char* const p;// p是const,*p可变char const* const p;// p和*p都是const//例如#include <stdio.h>#include <stdlib.h>#include <string.h>int main(void){ //const修饰一个变量为只读 const int a = 10; //a = 100; //err //指针变量, 指针指向的内存, 2个不同概念 char buf[] = "aklgjdlsgjlkds"; //从左往右看,跳过类型,看修饰哪个字符 //如果是*, 说明指针指向的内存不能改变 //如果是指针变量,说明指针的指向不能改变,指针的值不能修改 const char *p = buf; // 等价于上面 char const *p1 = buf; //p[1] = '2'; //err p = "agdlsjaglkdsajgl"; //ok char * const p2 = buf; p2[1] = '3'; //p2 = "salkjgldsjaglk"; //err //p3为只读,指向不能变,指向的内存也不能变 const char * const p3 = buf; return 0;} 三、数组越界问题 123456void test(){ char string[10];//应该是char string[11];//字符串数组最后一位是\\0 char*str="0123456789"; strcpy(string,str);//err} 四、C语言中宏定义的使用 ==预处理==命令可以改变程序设计环境,提高编程效率,它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对==预处理==之后的源程序进行==编译==处理,得到可供执行的目标代码。C 语言提供的预处理功能有三种,分别为==宏定义==、文件包含和条件编译。 123456789101112131415161718192021222324252627282930313233343536// 不带参数的宏定义#define MAX 10/*带参宏定义*/#define M(y) y*y+3*y/*宏调用*/k=M(5);//宏定义最大值#include <iostream>using namespace std;#define max(a,b) (((a)>(b))?(a):(b))int main(){ cout << max(1+1,2+2) << endl; return 0;}//这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。/* #define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//输入a++,a=a+2 调用两次#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )#define MAX( a, b ) ({int _a = a;int _b = b;(_a) > (_b)? (_a) :(_b); })*//*程序员对宏定义的使用要非常小心,特别要注意两个问题: (1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:#define MIN(A,B) (A) <= (B) ? (A) : (B)#define MIN(A,B) (A <= B ? A : B ) 都应判0分; (2)防止宏的副作用。 宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:((*p++) <= (b) ? (*p++) : (*p++)) 这个表达式会产生副作用,指针p会作三次++自增操作。 除此之外,另一个应该判0分的解答是:#define MIN(A,B) ((A) <= (B) ? (A) : (B)); 不能在后面加冒号。 参考文章:宏定义正确处理a++ 五、递归算N! 123456789101112131415161718192021#include <stdio.h> long fac(int n) { long f; if(n<0) printf("n<0,data error!"); else if(n==0,n==1) f=1; else f = fac(n-1)*n; return (f); } int main(){ long fac(int n); int n,y; printf("input an integer number:"); scanf("%d",&n); y= fac(n); printf("%d!=%ld\\n",n,y); return 0; } 六、中断服务子程序(ISR) 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展-让标准C支持中断。其代表事实是,产生了一个新的关键字 interrupt(51即如此)。下面的代码就使用了interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。 12345678910111213141516171819202122232425262728__interrupt double compute_area (double radius) {double area = PI * radius * radius;printf("/nArea = %f", area);return area;}/*中断服务程序需要满足如下要求: (1)不能返回值; (2)不能向ISR传递参数; (嵌入式中的ISR指的是中断服务处理)(3) ISR应该尽可能的短小精悍; (4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。这个函数有太多的错误了,以至让人不知从何说起了(前提是**非操作系统**下的中断服务函数):**1)ISR 不能返回一个值(都应该为void类型)。如果你不懂这个,那么你不会被雇用的。****2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。**3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是**短而有效率**的,在ISR中做浮点运算是不明智的。另外中断服务程序是运行在内核态的(linux),**内核通常是不支持浮点运算的**。*/ 七、位运算符 123456789101112//5=>0101,7=>01111)& 按位与//5&7==>0101,也就是52)| 按位或//5|7==>0111,也就是73)^ 按位异或//5^7==>0010,也就是24)~ 按位取反5)<< 左移6)>> 右移 八、register 关键词 寄存器存在于CPU内部,运算速度非常快, 因为内存中的数据必须载入寄存器才能计算。如果直接定义一个变量为寄存器变量,则少了载入等过程自然会快。对于频繁使用的变量可以把它放在寄存器中来提速度。1.寄存器变量可以用来优化加速c语言程序2.声名只需在类型前多加register 即可,eg register int quick; (quick 就是一个整形的寄存器变量)3.register只是一个建议型关键字,能不能声名成功还取决于编译器(建议型的关键字还有c++中的 inline),若不幸没有请求成功,则变量变成一个普通的自动变量。4.是无法对一个register变量取地址的(因为寄存器变量多放在寄存器而非内存中,内存有地址,而寄存器是无地址的) 12345//全局变量最好不要占用寄存器,会影响程序的速度register int num = 1000;//err//只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量。 //静态变量无法定义为寄存器变量,静态变量存在静态区register static double res = 0.0;//err 九、volatile 关键词 volatile的作用是告知编译器,它修饰的变量随时都可能被改变,因此,编译后的程序每次在使用该变量的值时,都会从变量的地址中读取数据,而不是从寄存器中获取。 12345678910111213141516171819/*volatile的本意是“易变的”由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:*/static int i=0;int main(void){...while (1){if (i) do_something();}}/* Interrupt service routine. */void ISR_2(void){i=1;}/*程序的本意是希望ISR_2中断产生时,在main当中调用do_something函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用。如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。*/ 一般说来,volatile用在如下的几个地方:1、中断服务程序中修改的供其它程序检测的变量需要加volatile;2、多任务环境下各任务间共享的标志应该加volatile;3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。 十、C 库函数 - memset() C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。 12345void *memset(void *str, int c, size_t n)//str -- 指向要填充的内存块。//c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。//n -- 要被设置为该值的字符数。//该值返回一个指向存储区 str 的指针。 十一、printf函数从右到左压栈 注意:函数printf从右到左压栈,然后将先读取放到栈底,最后读取的放在栈顶,处理时候是从栈顶开始的,所以我们看见的结果是,从右边开始处理的。 1234567#include <stdio.h>int main(){int i=10;int a=i;printf("%d %d",(a+i),(a=2*a));}//30,20 十二、排序算法 冒泡排序:基本思想:比较相邻的两个数,如果前者比后者大,则进行交换。每一轮排序结束,选出一个未排序中最大的数放到数组后面。 123456789101112131415161718192021#include<stdio.h>//冒泡排序算法void bubbleSort(int *arr, int n) { for (int i = 0; i<n - 1; i++) for (int j = 0; j < n - i - 1; j++) { //如果前面的数比后面大,进行交换 if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } }}int main() { int arr[] = { 10,6,5,2,3,8,7,4,9,1 }; int n = sizeof(arr) / sizeof(int); bubbleSort(arr, n); printf("排序后的数组为:\\n"); for (int j = 0; j<n; j++) printf("%d ", arr[j]); printf("\\n"); return 0; 参考文章:七大经典排序算法总结(C语言描述)","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"C","slug":"C","permalink":"https://kelecn.top/tags/C/"}]},{"title":"精简代码:改换运算符","slug":"15、精简代码:改换运算符","date":"2020-08-22T13:57:00.000Z","updated":"2021-04-03T06:53:46.000Z","comments":true,"path":"posts/38646/","link":"","permalink":"https://kelecn.top/posts/38646/","excerpt":"简介: 精简代码:改换运算符","text":"简介: 精简代码:改换运算符 1、位运算(&)替换取余(%)运算即:如果Y =,则X%Y可以变化为X&(Y-1)由于我们知道位运算比较高效,在某些情况下,当b为2的n次方时,有如下替换公式:a % b = a & (b-1)(b=2^n)即:a % 2^n = a & (2^n-1)例如:14%8,取余数,相当于取出低位,而余数最大为7,14二进制为1110,8的二进制1000,8-1 = 7的二进制为0111,由于现在低位全为1,让其跟14做&运算,正好取出的是其低位上的余数。1110&0111=110即6=14%8;(此公式只适用b=2^n,是因为可以保证b始终只有最高位为1,其他二进制位全部为0,减去1,之后,可以把高位1消除,其他位都为1,而与1做&运算,会保留原来的数。) 2、左移运算(<<)替换乘法(*)运算a=a8;b=b/8;可以改为:a=a<<3;b=b>>3;说明:除2 = 右移1位; 乘2 = 左移1位除4 = 右移2位; 乘4 = 左移2位除8 = 右移3位; 乘8 = 左移3位通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数才可以用移位的方法得到结果。如:a=a9 =a(8+1)=a8+a1可以改为:a=(a<<3)+a如:a=a7 =a(8-1)=a8-a1可以改为:a=(a<<3)-a 总结:a=an; n分解成(2^m + s),则a=an可以改为a=(a<<m)+as;as再同理分解替换。 例:a=a10 => a=a(8+2) => a=a8 + a2 => a=(a<<3)+(a<<1) 3、右移运算(>>)替换除法(/)运算a=a*8;b=b/8;可以改为:a=a<<3;b=b>>3;说明:除2 = 右移1位; 乘2 = 左移1位除4 = 右移2位; 乘4 = 左移2位除8 = 右移3位; 乘8 = 左移3位通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数才可以用移位的方法得到结果。如:a=a/9 =a/(8+1)=a/8+a/1可以改为:a=(a>>3)+a如:a=a/7 =a/(8-1)=a/8-a/1可以改为:a=(a>>3)-a总结:a=a/n; n分解成(2^m + s),则a=a/n可以改为a=(a>>m)+a/s;a/s再同理分解替换。例:a=a/10 => a=a/(8+2) => a=a/8 + a/2 => a=(a>>3)+(a>>1) 4、C/C++中的移位操作容易出错的情况: 什么样的数据类型可以直接移位 只有整型数据才能用移位替代乘除法,如:char、short、int、long、unsigned char、unsigned short、unsigned int、unsigned long。(double、float、bool、long double则不可以进行移位操作。) 有符号数据类型移位需要注意符号位对于char、short、int、long这些有符号的数据类型:对负数进行左移:符号位始终为1,其他位左移。对正数进行左移:所有位左移,即 <<,可能会变成负数对负数进行右移:取绝对值,然后右移,再取相反数对正数进行右移:所有位右移,即 >> 无符号数据类型的移位操作对于unsigned char、unsigned short、unsigned int、unsigned long这些无符号数据类型:没有特殊要说明的,使用<< 和 >> 操作符就OK了。5、参考资料: 使用位运算替换取余操作 移位操作与乘除法的关系","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"ARM","slug":"ARM","permalink":"https://kelecn.top/tags/ARM/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"C","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"单片机","slug":"单片机","permalink":"https://kelecn.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"}]},{"title":"基于PicGo的GitHub图床","slug":"14、我的GitHub图床(基于PicGo)","date":"2020-08-18T13:57:00.000Z","updated":"2021-08-24T15:31:09.100Z","comments":true,"path":"posts/10250/","link":"","permalink":"https://kelecn.top/posts/10250/","excerpt":"简介:GitHub图床(基于PicGo)","text":"简介:GitHub图床(基于PicGo) GitHub图床(基于PicGo)1. 首先你得有一个GitHub账号。注册GitHub就不用我多言。2. 新建一个仓库 记下你取的仓库名。 3. 生成一个token用于PicGo操作你的仓库:访问:https://github.com/settings/tokens 然后点击Generate new token。 把repo的勾打上即可。然后翻到页面最底部,点击Generate token的绿色按钮生成token。 注意:这个token生成后只会显示一次!你要把这个token复制一下存到其他地方以备以后要用。 4. 配置PicGo注意:仓库名的格式是用户名/仓库,比如我创建了一个叫做test的仓库,在PicGo里我要设定的仓库名就是kelecn/images。一般我们选择master分支即可。然后记得点击确定以生效,然后可以点击设为默认图床来确保上传的图床是GitHub。 12345678//PicGo配置{ "repo": "", // 仓库名,格式是username/reponame "token": "", // github token "path": "", // 自定义存储路径,比如img/ "customUrl": "", // 自定义域名,注意要加http://或者https:// "branch": "" // 分支名,默认是master} 至此配置完毕,已经可以使用了。当你上传的时候,你会发现你的仓库里也会增加新的图片了: 更多图床设计:请参考PicGo官方手册","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"相册","slug":"相册","permalink":"https://kelecn.top/tags/%E7%9B%B8%E5%86%8C/"},{"name":"图床","slug":"图床","permalink":"https://kelecn.top/tags/%E5%9B%BE%E5%BA%8A/"},{"name":"Github","slug":"Github","permalink":"https://kelecn.top/tags/Github/"},{"name":"PicGo","slug":"PicGo","permalink":"https://kelecn.top/tags/PicGo/"}]},{"title":"ARM处理器7种工作模式","slug":"12、ARM处理器7种工作模式","date":"2020-06-11T13:47:00.000Z","updated":"2021-04-03T06:53:18.000Z","comments":true,"path":"posts/4235/","link":"","permalink":"https://kelecn.top/posts/4235/","excerpt":"简介:ARM处理器7种工作模式(特权模式,异常模式,用户模式)。","text":"简介:ARM处理器7种工作模式(特权模式,异常模式,用户模式)。 ARM处理器7种工作模式(特权模式,异常模式,用户模式)用户模式(USR):正常程序执行模式,不能直接切换到其他模式系统模式(SYS):运行操作系统的特权任务,与用户模式类似,但具有可以直接切换到其他模式等特权快中断模式(FIQ):支持高速数据传输及通道处理,FIQ异常响应时进入此模式中断模式(IRQ):用于通用中断处理,IRQ异常响应时进入此模式管理模式(SVC):操作系统保护模式,系统复位和软件中断响应时进入此模式(由系统调用执行软中断SWI命令触发)中止模式(ABT):用于支持虚拟内存和/或存储器保护,在ARM7TDMI没有大用处未定义模式(UND):支持硬件协处理器的软件仿真,未定义指令异常响应时进入此模式 7种工作模式分类: 除用户模式外,其余6种工作模式都属于特权模式 特权模式中除了系统模式以外的其余5种模式称为异常模式 大多数程序运行于用户模式 进入特权模式是为了处理中断、异常、或者访问被保护的系统资源 硬件权限级别:系统模式 > 异常模式 > 用户模式快中断与慢中断区别:快中断处理时禁止中断","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"ARM","slug":"ARM","permalink":"https://kelecn.top/tags/ARM/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"处理器","slug":"处理器","permalink":"https://kelecn.top/tags/%E5%A4%84%E7%90%86%E5%99%A8/"},{"name":"硬件","slug":"硬件","permalink":"https://kelecn.top/tags/%E7%A1%AC%E4%BB%B6/"}]},{"title":"测试相册功能","slug":"13、相册功能测试","date":"2020-06-09T13:57:00.000Z","updated":"2021-08-15T15:46:30.656Z","comments":true,"path":"posts/39047/","link":"","permalink":"https://kelecn.top/posts/39047/","excerpt":"","text":"相册 壁纸二次元日常旅游","categories":[{"name":"生活","slug":"生活","permalink":"https://kelecn.top/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"相册","slug":"相册","permalink":"https://kelecn.top/tags/%E7%9B%B8%E5%86%8C/"},{"name":"Kelecn","slug":"Kelecn","permalink":"https://kelecn.top/tags/Kelecn/"}]},{"title":"C++与Java多态的区别","slug":"11、C++与Java多态的区别","date":"2020-06-06T13:40:00.000Z","updated":"2021-04-03T06:53:06.000Z","comments":true,"path":"posts/33552/","link":"","permalink":"https://kelecn.top/posts/33552/","excerpt":"简介:C++与Java多态的区别。","text":"简介:C++与Java多态的区别。 多态是指用父指针指向不同子类对象时,调用其共有的函数,不同的子类会有不同的行为。虽然C++和Java都具有多态机制,但是他们的实现不同,使用时的效果也会略有不同。在C++中 普通函数调用:具体调用哪个方法在编译时就可以决定(通过查找编译器的符号表),同时在使用标准过程调用机制基础上增加一个表示对象身份的指针(this指针)。 虚函数调用:函数调用依赖于对象的实际类型,一般地说,对象的实际类型只能在运行时间才能确定。实现机制是使用virtual table(vtbls)和virtual table pointers(vptrs)。 vtbl 是由函数指针构成的数组或链表,程序中每一个class凡声明(或继承)虚函数者,都有一个自己的vtbl,其中的条目就是该class的各个虚函数实现的指针。因此必须为每一个class消耗一个vtbl空间,其大小视虚函数的个数确定。 凡声明有虚函数的class,其对象都有一个隐藏的data member,用来指向class的vtbl。 当多态发生时,编译器首先根据对象vptr找出其vtbl,然后找出vtbl内对应的函数指针,最后调用函数指针指向的函数。从而实现多态。 在Java中1.C++中VTable和vptr是在编译阶段由编译器自动生成的,也就是说,在C++程序载入内存以前,在.obj(.o)文件中已经有这些结构的信 息;Java中的方法表是由JVM生成的,因此,使用javac命令编译后生成的.class文件中并没有方法表的信息。只有等JVM把.class文件 载入到内存中时,才会为该.class文件动态生成一个与之关联的方法表,放置在JVM的方法区中。 2.C++中某个方法在VTable的索引号是在编译阶段已经明确知道的,并不需要在运行过程中动态获知;Java中的方法初始时都只是一个符号,并不是 一个明确的地址,只有等到该方法被第一次调用时,才会被解析成一个方法表中的偏移量,也就是说,只有在这个时候,实例方法才明确知道自己在方发表中的偏移 量了,在这之前必须经历一个解析的过程。 因此在构造函数是Java会发生多态,即使子类此时还没有构造完全(一个极难发现的bug)。而C++则不会发生多态,待父类构造完全,在构造子类。Java代码如下: 123456789101112131415161718192021222324252627public class A { public void fun() { System.out.println("A"); } public A() { this.fun(); }}public class B extends A{ public void fun() { System.out.println("B"); } public B() { fun(); } public static void main(String [] argv) { B b = new B(); }}//B//B C++代码如下: 123456789101112131415161718192021222324252627282930313233343536#include <iostream>#include <string>#include <cstring>#include <cstdlib>using namespace std;class A{ public: virtual void fun() { cout<<"A"<<endl; } A() { fun(); }};class B : A{ public: virtual void fun() { cout<<"B"<<endl; } B() { fun(); } };int main(int argc, char *argv[]){ B b;}//A//B 可以发现,C++构造子类时,先构造父类,输出A,然后在构造自身,输出B。而Java在构造子类时,父类并未构造完成,但已经可以发生多态输出B,然后再构造自身,输出B。Java一般为了避免这种情况,会把fun等init()函数声明为private或者finial。","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"Java","slug":"Java","permalink":"https://kelecn.top/tags/Java/"}]},{"title":"C++重载注意点","slug":"10、重载注意点","date":"2020-05-20T13:30:00.000Z","updated":"2021-04-03T06:52:22.000Z","comments":true,"path":"posts/18002/","link":"","permalink":"https://kelecn.top/posts/18002/","excerpt":"简介:C++重载注意点。","text":"简介:C++重载注意点。 1.调用函数在前,定义函数在后,进行原型声明2.函数重载注意:参数类型和个数相同,参数顺序不同,也可以重载12345678910111213141516171819202122232425#include <iostream>#include <iomanip> using namespace std; void print(int a,double b);//调用函数在前,定义函数在后,进行原型声明void print (double b,int a);//调用函数在前,定义函数在后,进行原型声明 int main(){ int x=8; double y=8; print(x,y); print(y,x); return 0;}void print(int a,double b){ cout<<showpoint<<"1"<<a<<b<<endl;} void print (double b,int a){ cout<<showpoint<<"2"<< a<<b <<endl; } 3.C++中cout输出字符型指针地址值的方法1234567891011//C++中cout输出字符型指针地址值的方法//若要打印地址请用void*,否则 p会被认为是字符串。原因:运算符重载的匹配规则 #include<iostream> using namespace std; int main() { char a; char *p=&a; cout<<(void*)p<<endl<<a<<endl; }","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"}]},{"title":"C++返回对象和返回引用","slug":"9、C++返回对象和返回引用","date":"2020-04-19T13:20:07.000Z","updated":"2021-04-03T06:52:08.000Z","comments":true,"path":"posts/10995/","link":"","permalink":"https://kelecn.top/posts/10995/","excerpt":"简介:C++返回对象和返回引用。","text":"简介:C++返回对象和返回引用。 我们发现,在C++中,有些成员函数返回的是对象,而有些函数返回的又是引用。 返回对象和返回引用的最主要的区别就是函数原型和函数头。 123Car run(const Car &) //返回对象Car & run(const Car &) //返回引用 返回对象会涉及到生成返回对象的副本。因此,返回对象的时间成本包括了调用复制构造函数来生成副本所需的时间和调用析构函数删除副本所需的时间。返回引用可以节省时间和内存。直接返回对象与按值传递对象类似,他们都生成临时副本。同样,返回引用与按引用传递对象类似,调用和被调用的函数对同一个对象进行操作。 并不是总是可以返回引用的。比如函数不能返回在函数中创建的临时对象的引用。因为当函数结束调用时,临时对象将消失,因此这种引用是非法的。在这种情况下,应返回对象,以生成一个调用程序可以使用的副本。","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"}]},{"title":"关于free和delete的使用","slug":"8、关于free和delete的使用","date":"2020-03-29T13:10:00.000Z","updated":"2021-04-03T06:51:54.000Z","comments":true,"path":"posts/26216/","link":"","permalink":"https://kelecn.top/posts/26216/","excerpt":"简介:关于free和delete的使用","text":"简介:关于free和delete的使用 两个同时存在是有它的原因的,free是函数,它只释放内存,但不会调用析构函数,如果用free去释放new申请的空间,会因为无法调用析构函数而出现不必要的错误。 12345678char *point = (char *) malloc(100); strcpy(point, “hello”); free(point); // Be careful here,point 所指的内存被释放,but point 所指的地址仍然不变 … if(point != NULL) // 没有起到防错作用 { strcpy(point, “jackery”); // error } 这段程序中,原来free和delete只是把指针所指的内存给释放掉,但并没有把指针本身干掉。指针point被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,point成了“野指针”。如果此时不把point设置为NULL,会让人误以为point是个合法的指针。如果程序比较长,我们有时记不住 point 所指的内存是否已经被释放,在继续使用point 之前,通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便point不是NULL指针,它也不指向合法的内存块。为了避免失误,最好在free之后或者之前,将指针指向NULL new()/delete()与 malloc()/free() 区别1.malloc()/free() 是标准库函数, 使用前需调用库头文件 <stdlib.h> 方可使用;而 new/delete 是运算 符,执行效率更高。 2.malloc() 需要手工计算字节数;而 new 能够自动计算需要分配的内存空间。 3.malloc() 返回的指针是 void 类型;而 new*返回的指针是它分配空间的类型。 4.new 时调用构造函数,而 malloc() 不能;delete 时调用析构函数,而 free() 不能。 5.new 在申请单个类型变量时可以赋初值,而 malloc() 不具备。","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"}]},{"title":"常量指针与指针常量","slug":"7、常量指针与指针常量","date":"2020-03-11T13:07:00.000Z","updated":"2021-04-03T06:51:44.000Z","comments":true,"path":"posts/8326/","link":"","permalink":"https://kelecn.top/posts/8326/","excerpt":"简介:常量指针与指针常量的区别。","text":"简介:常量指针与指针常量的区别。 常量指针(被指向的对象是常量)定义:又叫常指针,可以理解为常量的指针,指向的是个常量 关键点: 常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改; 常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值; 指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址; 代码形式:1int const* p; const int* p; 12345678910111213141516#include <stdio.h>// 常量指针(被指向的对象是常量)int main() { int i = 10; int i2 = 11; const int *p = &i; printf("%d\\n", *p);//10 i = 9; //OK,仍然可以通过原来的声明修改值, //Error,*p是const int的,不可修改,即常量指针不可修改其指向地址 //*p = 11; //error: assignment of read-only location ‘*p’ p = &i2;//OK,指针还可以指向别处,因为指针只是个变量,可以随意指向; printf("%d\\n", *p);//11 return 0;} 指针常量(指针本身是常量)定义:本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。 关键点: 它是个常量! 指针所保存的地址可以改变,然而指针所指向的值却不可以改变; 指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化; 代码形式:1int* const p; 1234567891011121314//指针常量(指针本身是常量)#include <stdio.h>int main() { int i = 10; int *const p = &i; printf("%d\\n", *p);//10 //Error,因为p是const 指针,因此不能改变p指向的内容 //p++;//error: increment of read-only variable ‘p’ (*p)++; //OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化 printf("%d\\n", *p);//11 i = 9;//OK,仍然可以通过原来的声明修改值, return 0;} 如何区分常量指针和指针常量 一种方式是看 * 和 const 的排列顺序,比如 123int const* p; //const * 即常量指针const int* p; //const * 即常量指针int* const p; //* const 即指针常量 还一种方式是看const离谁近,即从右往左看,比如 123int const* p; //const修饰的是*p,即*p的内容不可通过p改变,但p不是const,p可以修改,*p不可修改;const int* p; //同上int* const p; //const修饰的是p,p是指针,p指向的地址不能修改,p不能修改,但*p可以修改; 例子12345678910111213141516171819202122232425//结构体类型的定义struct stu{ char name[50]; int age;};void fun1(struct stu * const p){ //p = NULL; //err p->age = 10; //ok}//void fun2(struct stu const* p)void fun2(const struct stu * p){ p = NULL; //ok //p->age = 10; //err}void fun3(const struct stu * const p){ //p = NULL; //err //p->age = 10; //err}","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"}]},{"title":"编程过程中遇到的一些问题总结(C++&Java)","slug":"6、编程过程中遇到的一些问题总结(C++&Java)","date":"2020-02-18T02:05:00.000Z","updated":"2021-04-03T06:51:34.000Z","comments":true,"path":"posts/6416/","link":"","permalink":"https://kelecn.top/posts/6416/","excerpt":"简介:(C++&Java)问题总结。","text":"简介:(C++&Java)问题总结。 1.Java中的boolean类型变量,只有两个值true ,false;C++也是,不过也可以用 1 0 代替。 12345678//Java boolean 类型public class HelloWorld { public static void main(String[] args) { while (true) { System.out.println("I Love YOU!"); } }} 1234567891011//C++ boolean 类型#include <iostream>using namespace std;int main(int argc, char *argv[]){ while (1) { cout<<"I Love YOU!"<<endl; } return 0;} 2.Java一个类中的boolean类型变量,一般用isXxx()类型获取私有变量Xxx; 注:get开头的方法,一般都表示返回某一个属性值;is开头的方法,一般都是用来表示判断某某内容。 123public boolean isSex() { return sex; }","categories":[{"name":"总结","slug":"总结","permalink":"https://kelecn.top/categories/%E6%80%BB%E7%BB%93/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"Java","slug":"Java","permalink":"https://kelecn.top/tags/Java/"}]},{"title":"JAVA测试题","slug":"5、JAVA测试题","date":"2020-02-07T02:05:00.000Z","updated":"2021-04-03T06:51:22.000Z","comments":true,"path":"posts/19203/","link":"","permalink":"https://kelecn.top/posts/19203/","excerpt":"简介:JAVA基础测试题。","text":"简介:JAVA基础测试题。 第一题输出9*9口诀表。 代码: 12345678910111213141516package exam;public class Exam { public static void main(String[] args) { for(int i = 1; i < 10; i ++) { for( int j = 1; j <= i; j++) { System.out.print(i +" * "+j+" = "+i*j + " "); } System.out.println();//换行 } }} 运行截图: 第二题求1+2!+3!+…+20!的和。 代码: 12345678910111213141516package exam;public class Exam { public static void main(String[] args) { float s=0,t=1; int n; for (n=1;n<=20;n++) { t=t*n; // 求n! s=s+t; // 将各项累加 } System.out.println("1+2!+3!+...+20!="+s); } } 运行截图: 第三题一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。 代码: 123456789101112131415161718192021222324package exam;import java.util.Scanner;public class Exam { public static void main(String[] args) { int ge,shi,qian,wan,x; System.out.println("请输入您想判断的五位数字:"); Scanner in = new Scanner(System.in); x=in.nextInt(); wan=x/10000;//万位 qian=x%10000/1000;//千位 shi=x%100/10;//十位 ge=x%10;//个位 if (ge==wan&&shi==qian)//个位等于万位并且十位等于千位 { System.out.println("这是回文数\\n"); } else { System.out.println("这是不是回文数\\n"); } } } 运行截图: 第四题一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高? 代码: 1234567891011121314151617package exam;public class Exam { public static void main(String[] args) { double sum = 0, high = 100;//sum为路径总和,high表示当前高度 for (int i = 0; i < 10; i++) { sum = high + high / 2 + sum;//一次落地距离+弹起距离+已经过路程 high /= 2;//弹起高度为一半 } sum -= high;//求第10次落地经过路程需减去第10次弹起距离 System.out.println("共经过:"+sum+ "米\\n"+"第10次反弹高度为:"+high+"米"); } } 运行截图: 第五题猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个; 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。 代码: 123456789101112131415package exam;public class Exam { public static void main(String[] args) { int x = 1;//第十天剩余桃子数 int y;//天数 for(y=1;y<=9;y++) { x=(x+1)*2;//前一天的剩余桃子数都是今天剩余桃子数加1后的两倍 } System.out.println("猴子第一天一共摘了"+x+"个桃子。"); } } 运行截图:","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"Java","slug":"Java","permalink":"https://kelecn.top/tags/Java/"}]},{"title":"面向对象的程序设计实验(C++)","slug":"4、面向对象的程序设计","date":"2019-12-25T02:51:34.000Z","updated":"2021-04-03T06:51:06.000Z","comments":true,"path":"posts/29708/","link":"","permalink":"https://kelecn.top/posts/29708/","excerpt":"简介:面向对象程序设计(C++)。","text":"简介:面向对象程序设计(C++)。 还没写呢^_^","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"}]},{"title":"嵌入式开发为什么选择C语言?","slug":"3、嵌入式开发为什么选择C语言?","date":"2019-11-25T09:08:29.000Z","updated":"2021-04-03T06:50:54.000Z","comments":true,"path":"posts/63638/","link":"","permalink":"https://kelecn.top/posts/63638/","excerpt":"简介:嵌入式+C语言=?","text":"简介:嵌入式+C语言=? 1.从语言特点来说①C语言有出色的可移植性,能在多种不同体系结构的软/硬平台上运行。 ②简洁紧凑,使用灵活的语法机制,并能直接访问硬件能够直接访问硬件的语言有:汇编和C语言汇编属于低级语言,难以完成一些复杂的功能,但是汇编比C语言访问硬件的效率更高。所以,一般将硬件初始化的工作交给汇编,比较复杂的操作交给C语言。 ③C语言具有很高的运行效率。 2.嵌入式开发中的地位——开发工具3.高级语言中的低级语言:面向过程VS面向对象面向过程:“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。“面向过程”也可称之为“面向记录”编程思想,他们不支持丰富的“面向对象”特性(比如继承、多态),并且它们不允许混合持久化状态和域逻辑。 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。面向过程其实是最为实际的一种思考方式,就算是面向对象的方法也是含有面向过程的思想。 可以说面向过程是一种基础的方法,它考虑的是实际地实现。一般的面向过程是从上往下步步求精,所以面向过程最重要的是模块化的思想方法。 面向对象:面向对象的分析根据抽象关键的问题域来分解系统。面向对象的设计是一种提供符号设计系统的面向对象的实现过程,它用非常接近实际领域术语的方法把系统构造成“现实世界”的对象。 面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想,这与传统的思想刚好相反:传统的程序设计主张将程序看作一系列函数的集合,或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象,因此它们都可以被看作一个小型的“机器”,即对象。 面向过程和面向对象的区别:以一个人从A地到B地为例,面向过程就是需要规划路线,了解路况,自己做好一系列的准备;而面向对象就是坐上一辆出租车,告诉司机我要去B地就可以了,不用关心其他的事情。 最后,C语言也有他自身的缺陷,比如代码的复用性差,代码的维护性差,扩展性(新增代码时不改变原来的代码)很差。","categories":[{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"}],"tags":[{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"硬件","slug":"硬件","permalink":"https://kelecn.top/tags/%E7%A1%AC%E4%BB%B6/"},{"name":"C语言","slug":"C语言","permalink":"https://kelecn.top/tags/C%E8%AF%AD%E8%A8%80/"}]},{"title":"Matlab Gui关于edit keypressfcn的响应","slug":"2、Matlab gui关于edit keypressfcn的响应","date":"2019-10-02T04:00:00.000Z","updated":"2021-04-03T06:50:42.000Z","comments":true,"path":"posts/17834/","link":"","permalink":"https://kelecn.top/posts/17834/","excerpt":"简介:Matlab GUI键盘输入相关代码。","text":"简介:Matlab GUI键盘输入相关代码。 1234567891011121314 function showmap fig=figure( 'Name','Timer','Position',[0,0,500,500] ,'NumberTitle','off','visible','off'); movegui(fig,'center'); set(fig,'visible','on'); ttext= uicontrol('Style','edit','Position',[150,250,200,30],'String','Press "Start"',... 'KeyPressFcn',@keyPress); ii=0; function keyPress(x,y) set(ttext,'string',num2str(ii)); ii = ii+1; endend 参考文献:打浦桥程序员","categories":[{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"Matlab","slug":"Matlab","permalink":"https://kelecn.top/tags/Matlab/"},{"name":"Gui","slug":"Gui","permalink":"https://kelecn.top/tags/Gui/"}]},{"title":"Kelecn的第一条博客!","slug":"1、可乐的第一条博客","date":"2019-09-19T09:10:00.000Z","updated":"2021-04-03T06:50:26.000Z","comments":true,"path":"posts/59565/","link":"","permalink":"https://kelecn.top/posts/59565/","excerpt":"简介:您好!Hexo!","text":"简介:您好!Hexo! 123456789101112131415161718 .::::. .::::::::. ::::::::::: ..:::::::::::' '::::::::::::' .:::::::::: '::::::::::::::.. ..::::::::::::. ``:::::::::::::::: ::::``:::::::::' .:::. ::::' ':::::' .::::::::. .::::' :::: .:::::::'::::. .:::' ::::: .:::::::::' ':::::. .::' :::::.:::::::::' ':::::. .::' ::::::::::::::' ``::::. ...::: ::::::::::::' ``::.````':. ':::::::::' ::::.. '.:::::' ':'````.. 友情链接:My Github、Hexo","categories":[{"name":"生活","slug":"生活","permalink":"https://kelecn.top/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"Kelecn","slug":"Kelecn","permalink":"https://kelecn.top/tags/Kelecn/"},{"name":"日记","slug":"日记","permalink":"https://kelecn.top/tags/%E6%97%A5%E8%AE%B0/"}]}],"categories":[{"name":"生活","slug":"生活","permalink":"https://kelecn.top/categories/%E7%94%9F%E6%B4%BB/"},{"name":"美化","slug":"美化","permalink":"https://kelecn.top/categories/%E7%BE%8E%E5%8C%96/"},{"name":"技术","slug":"技术","permalink":"https://kelecn.top/categories/%E6%8A%80%E6%9C%AF/"},{"name":"知识","slug":"知识","permalink":"https://kelecn.top/categories/%E7%9F%A5%E8%AF%86/"},{"name":"总结","slug":"总结","permalink":"https://kelecn.top/categories/%E6%80%BB%E7%BB%93/"}],"tags":[{"name":"旅行","slug":"旅行","permalink":"https://kelecn.top/tags/%E6%97%85%E8%A1%8C/"},{"name":"打卡","slug":"打卡","permalink":"https://kelecn.top/tags/%E6%89%93%E5%8D%A1/"},{"name":"深圳","slug":"深圳","permalink":"https://kelecn.top/tags/%E6%B7%B1%E5%9C%B3/"},{"name":"深圳文和友","slug":"深圳文和友","permalink":"https://kelecn.top/tags/%E6%B7%B1%E5%9C%B3%E6%96%87%E5%92%8C%E5%8F%8B/"},{"name":"Hexo","slug":"Hexo","permalink":"https://kelecn.top/tags/Hexo/"},{"name":"Volantis","slug":"Volantis","permalink":"https://kelecn.top/tags/Volantis/"},{"name":"Botui","slug":"Botui","permalink":"https://kelecn.top/tags/Botui/"},{"name":"聊天机器人","slug":"聊天机器人","permalink":"https://kelecn.top/tags/%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA/"},{"name":"JavaScript","slug":"JavaScript","permalink":"https://kelecn.top/tags/JavaScript/"},{"name":"嵌入式","slug":"嵌入式","permalink":"https://kelecn.top/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/"},{"name":"OpenMV","slug":"OpenMV","permalink":"https://kelecn.top/tags/OpenMV/"},{"name":"机器视觉","slug":"机器视觉","permalink":"https://kelecn.top/tags/%E6%9C%BA%E5%99%A8%E8%A7%86%E8%A7%89/"},{"name":"ESP8266","slug":"ESP8266","permalink":"https://kelecn.top/tags/ESP8266/"},{"name":"STM32","slug":"STM32","permalink":"https://kelecn.top/tags/STM32/"},{"name":"NodeMCU","slug":"NodeMCU","permalink":"https://kelecn.top/tags/NodeMCU/"},{"name":"Web","slug":"Web","permalink":"https://kelecn.top/tags/Web/"},{"name":"内置壁纸","slug":"内置壁纸","permalink":"https://kelecn.top/tags/%E5%86%85%E7%BD%AE%E5%A3%81%E7%BA%B8/"},{"name":"Wallpaper","slug":"Wallpaper","permalink":"https://kelecn.top/tags/Wallpaper/"},{"name":"Google","slug":"Google","permalink":"https://kelecn.top/tags/Google/"},{"name":"数码","slug":"数码","permalink":"https://kelecn.top/tags/%E6%95%B0%E7%A0%81/"},{"name":"Github","slug":"Github","permalink":"https://kelecn.top/tags/Github/"},{"name":"捐赠","slug":"捐赠","permalink":"https://kelecn.top/tags/%E6%8D%90%E8%B5%A0/"},{"name":"jsDelivr","slug":"jsDelivr","permalink":"https://kelecn.top/tags/jsDelivr/"},{"name":"CDN","slug":"CDN","permalink":"https://kelecn.top/tags/CDN/"},{"name":"缓存","slug":"缓存","permalink":"https://kelecn.top/tags/%E7%BC%93%E5%AD%98/"},{"name":"硬件","slug":"硬件","permalink":"https://kelecn.top/tags/%E7%A1%AC%E4%BB%B6/"},{"name":"Python","slug":"Python","permalink":"https://kelecn.top/tags/Python/"},{"name":"C++","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"TCP","slug":"TCP","permalink":"https://kelecn.top/tags/TCP/"},{"name":"UDP","slug":"UDP","permalink":"https://kelecn.top/tags/UDP/"},{"name":"QT","slug":"QT","permalink":"https://kelecn.top/tags/QT/"},{"name":"C语言","slug":"C语言","permalink":"https://kelecn.top/tags/C%E8%AF%AD%E8%A8%80/"},{"name":"PID","slug":"PID","permalink":"https://kelecn.top/tags/PID/"},{"name":"算法","slug":"算法","permalink":"https://kelecn.top/tags/%E7%AE%97%E6%B3%95/"},{"name":"内存","slug":"内存","permalink":"https://kelecn.top/tags/%E5%86%85%E5%AD%98/"},{"name":"SPI","slug":"SPI","permalink":"https://kelecn.top/tags/SPI/"},{"name":"I2C","slug":"I2C","permalink":"https://kelecn.top/tags/I2C/"},{"name":"USB","slug":"USB","permalink":"https://kelecn.top/tags/USB/"},{"name":"总线","slug":"总线","permalink":"https://kelecn.top/tags/%E6%80%BB%E7%BA%BF/"},{"name":"CISC","slug":"CISC","permalink":"https://kelecn.top/tags/CISC/"},{"name":"RISC","slug":"RISC","permalink":"https://kelecn.top/tags/RISC/"},{"name":"指令集","slug":"指令集","permalink":"https://kelecn.top/tags/%E6%8C%87%E4%BB%A4%E9%9B%86/"},{"name":"Boost","slug":"Boost","permalink":"https://kelecn.top/tags/Boost/"},{"name":"Buck","slug":"Buck","permalink":"https://kelecn.top/tags/Buck/"},{"name":"DCDC","slug":"DCDC","permalink":"https://kelecn.top/tags/DCDC/"},{"name":"计算机网络","slug":"计算机网络","permalink":"https://kelecn.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"},{"name":"编程","slug":"编程","permalink":"https://kelecn.top/tags/%E7%BC%96%E7%A8%8B/"},{"name":"C","slug":"C","permalink":"https://kelecn.top/tags/C/"},{"name":"ARM","slug":"ARM","permalink":"https://kelecn.top/tags/ARM/"},{"name":"单片机","slug":"单片机","permalink":"https://kelecn.top/tags/%E5%8D%95%E7%89%87%E6%9C%BA/"},{"name":"相册","slug":"相册","permalink":"https://kelecn.top/tags/%E7%9B%B8%E5%86%8C/"},{"name":"图床","slug":"图床","permalink":"https://kelecn.top/tags/%E5%9B%BE%E5%BA%8A/"},{"name":"PicGo","slug":"PicGo","permalink":"https://kelecn.top/tags/PicGo/"},{"name":"处理器","slug":"处理器","permalink":"https://kelecn.top/tags/%E5%A4%84%E7%90%86%E5%99%A8/"},{"name":"Kelecn","slug":"Kelecn","permalink":"https://kelecn.top/tags/Kelecn/"},{"name":"面向对象","slug":"面向对象","permalink":"https://kelecn.top/tags/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"},{"name":"代码","slug":"代码","permalink":"https://kelecn.top/tags/%E4%BB%A3%E7%A0%81/"},{"name":"Java","slug":"Java","permalink":"https://kelecn.top/tags/Java/"},{"name":"Matlab","slug":"Matlab","permalink":"https://kelecn.top/tags/Matlab/"},{"name":"Gui","slug":"Gui","permalink":"https://kelecn.top/tags/Gui/"},{"name":"日记","slug":"日记","permalink":"https://kelecn.top/tags/%E6%97%A5%E8%AE%B0/"}]}