192+4K的SRAM够用吗

MIPS32 M4K处理器内核SRAM接口应用
> MIPS32 M4K处理器内核SRAM接口应用
MIPS32 M4K处理器内核SRAM接口应用
  环境要求在尽可能小的封装里实现最多的通用。存尺寸和成本的限制下,M4K内核内部不支持指令高速缓存(I-cache)或数据高速缓存(D-cache)的标准功能。但 M4K内核所具有的一些特点使其非常适用于应用领域。这就涉及到本文重点讨论的一个内容--,这是 M4K内核的一个标准功能。本文引用地址:
  微控制器应用需要仅用最少的逻辑就可以在处理器内核和存储系统之间实现紧密耦合。M4K内核就是一个比较完美的解决方案。
  M4K内核接口基本描述
  M4K内核SRAM接口是M4K内核的通用高速存储器接口。它可为指令存储器和数据存储器路径提供低延迟接口,支持单周期和多周期存储器存取。 必须指出,SRAM接口不能直接与外部存储器件连接,若要实现外部存储连接,需使用一个外部存储控制器。必须使用固定映射表(FMT)和SRAM接口,以提供完整的存储器控制逻辑。
  双模操作
  SRAM接口的初始配置称为双模。在这种模式下,指令和数据通道彼此隔离。数据有独立的读写总线(D-SRAM),还有D-SRAM接口控制信号和一个独立的指令侧(I-SRAM)接口,以及其互补I-SRAM控制信号。
  I-SRAM接口具有改变信号输入方向的能力,需要时,可将D-SRAM读周期改变到I侧方向。这有助于改良的Harvard架构的实现,和非易失性数据在程序存储器中的存储。
  双模有助于I-SRAM和D-SRAM接口的同步处理,消除任何可能出现在公用总线接口上的延迟,防止其减缓程序的执行。在这个模式下,内核可达到1.5 DMIPS/MHz的标称性能。双模结构如图1所示。
  标准模式
  标准模式是标准接口的一个配置选项,在这种模式下,为了节省必须从内核发送的信号总数,I-SRAM和D-SRAM信号合并在一起。除了数据写总线外,D-SRAM接口是完全禁用的,所有数据读周期都自动改变方向,以使用I-SRAM读总线。
  在该模式中,内核的平均性能大概是1.2 DMIPS/MHz。然而,由SRAM接口内核暴露的活跃信号总数是122个,比双模节省87个信号。减少接口使用的活跃信号数量,对于成本非常有限、总裸片面积比绝对性能更重要的设计来说,是一种更经济有效的方式。
  SRAM接口标准模式如图2所示。
  处理中断
  SRAM接口提供了中止指令处理的能力,这种能力可中止M4K内核5段流水线中任何指令处理。这有助于外部系统控制器立即响应外部事件,如中断请求或通过EJTAG调试接口请求。在处理典型微控制器应用中高度确定性性质时,快速响应外部中断事件至关重要。
  对接口信号连接有限的器件提供支持
  M4K处理器内核可通过特定字节通道,对接口信号连接有限的存储器件接口的能力进行控制,可帮助M4K内核以连续字节读取汇编32位字,然后在SRAM接口中进行内部汇编。
  这个功能通常用于接口外设的总线锁存和总线停止的组合,如ADC。该类型的混合信号器件通常不提供32位位宽的接口。
  总线锁存和总线停止
  SRAM接口提供输入控制信号,在外部存储控制器禁用这个信号之前锁住数据总线,以防止CPU进一步写处理。由此,可不用担心多线程存储周期的损害。SRAM接口还提供控制信号,利于总线停止,使设计师将速度更慢的存储器和外设连接到系统成为可能。这些器件可包括速度更慢的非易失性RAM和混合信号器件,在将请求的数据传送到总线之前,它们需要更多的等待时间。
  缓存关联信号
  虽然M4K内核不包含任何高速缓存,但仍能指明出现在地址总线上的当前存储器地址是否可以进行缓存。外部存储控制器能够利用这些状态信号实现L2高速缓存结构。
  M4K SRAM接口在微控制器领域的应用
  利用上述这些功能,设计师们能够对于微控制器系统环境中使用M4K SRAM接口的多种优势有所认识。
  首先,紧密耦合的接口意味着大多数处理任务是在单时钟周期内完成的。除非当设计师执行总线停止时,需要考虑速度较慢的存储器件的完成周期。而且,连接到该接口的控制逻辑必须只能处理指令和数据,以使整个逻辑设计不那么复杂。另外,可能还要锁住总线,使原子处理的完成能够不受排队周期的影响。
  固定映射表(FMT)可减少外部存储控制器所需的逻辑和解码量。除了存储器映像外设,以及对微控制器可用的存储器件绝对尺寸外,有源区都在定义的边界之内。
  在双模操作下,指令提取路径和数据读/写路径是独立的。这些独立的数据通道有助于存储控制器逻辑优化存储器件的类型和尺寸。
  中止流水线中,任何处理的能力都有助于在调试环境中实现快速响应和精确的断点控制。
  基本上,L2缓存可以使用M4K内核的本地信号执行,从而简化L2缓存控制器。
  MIPS32M4K内核SRAM接口为M4K内核提供了一种高速、易于使用和高度可配置的存储器接口。除了指令和数据存储器,它不包含额外的开销,如处理任何情况的协议或信号等,有助于芯片设计师用极少的外部逻辑实现M4K内核的最大性能。最终,这种能力代表着以最低的成本实现更高性能的微控制器系统。
分享给小伙伴们:
我来说两句……
微信公众号二
微信公众号一&figure&&img src=&https://pic2.zhimg.com/v2-6c8b4efa461b01aed752a739f1086768_b.jpg& data-rawwidth=&550& data-rawheight=&386& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&https://pic2.zhimg.com/v2-6c8b4efa461b01aed752a739f1086768_r.jpg&&&/figure&&p&从大一入学被调剂到计算机专业,到喜欢上这个专业,再到毕业拿到10多个offer,最终进入理想的大厂工作。回想起来这些年确确实实踩了很多坑。&b&我刚开始学习编程的时候也想一口吃成一个胖子,想速成,但是有时候却是不尽人意。&/b&&/p&&p&回忆了下这几年学习编程的过程,整理了一些我自己认为很需要注意的几个方面,分享给大家。希望能让初学编程的你,少走一些弯路,&b&可能文章比较长,但我真心希望初学编程的你能够认真看完,至少,我认为如果我刚学编程的时候看到这篇文章,对我或多或少是有一些帮助的。&/b&&/p&&p&我个人是一名计算机专业的学生,很多人可能会认为我是在课堂上学到的编程,其实不是这样。&/p&&p&我认为科班出身和非科班出身的学生最大的区别在于&b&科班出身的学生知道去学什么&/b&,知道每一门课程是干什么的;还有一些必须完成的作业、小项目,促使他们去做一些实际的编码练习,除此之外,真的全靠自学。&/p&&p&对于自学编程,我认为首先应该谈的是如何去避免一些坑,这样就可能节约大把的时间。下面我就以问题的形式来分享一些我认为重要的方面。&/p&&p&&br&&/p&&p&&b&&i&1. 我应该选择什么编程语言&/i&&/b&&/p&&p&可能困扰编程新手最多的一个问题是【我应该学什么编程语言】或者【我需要学习哪些课程才能做出一个web、一个app】,很多人一直纠结这个问题,陷入了东学一点、西看一点的死循环,到头来啥也没学好,这会很浪费时间。&/p&&p&刚上大一的时候,我也很想知道应该选择什么编程语言。我问了很多人,网上各种查资料,但所能得到的答案都很片面,多数对这个问题答非所问,总是回答说“某某编程语言难”,“某某编程语言性能好”。&b&其实作为初学者,我们对计算机体系都不了解,就不要过多地去纠结性能,或者难易等因素&/b&,原因我等下再说。&/p&&p&如果你有明确的方向,那么很好选择。&b&如果你想做算法、机器学习方向,那么python是最好的选择。如果你想做web开发,java、php等都可以。如果想做一些更底层的工作,那么就可以选c。&/b&当然这是建立在你有明确方向的基础上。可是,很多人都没怎么接触过计算机行业,特别是和我一样刚入学就被调剂到计算机专业的人。对这些同学来说,各个编程语言就只是个名字,除了叫法不一样,你根本不知道它们有什么差别。所以索性不要纠结了,我替你选一个吧。&/p&&p&&b&如果你是在校大学生,那么你有大把连续的时间,就先学习c,然后再学c++。&/b&我个人是学c入门的,也许很多人不理解我为什么推荐学c,因为c和c++都很难、很复杂,看起来并不适合入门。然而正是它们的难和复杂才能让你更好地理解计算机系统【&b&计算机系统不是指操作系统】&/b&。&b&学习编程不是学习编程语言,而是学习一个计算机生态,即一个庞大的知识体系。&/b&只会编程语言而不理解整个计算机的体系,就像只会写字而写不出好文章。了解c/c++和了解计算机系统是极为贴合的,&b&&i&向下&/i&&/b&可以帮助你更容易地理解操作系统、编译原理、计算机网络、计算机组成原理,为什么呢?因为较为底层的东西很多都是用c实现的,和系统的贴合度极高,很多教材源码甚至教程,在讲述这些知识的时候都是用c或c++作为媒介。而&b&&i&向上&/i&&/b&,c++面向对象的机制,也可以做出一些应用,譬如五子棋游戏等,也不会显得那么枯燥。花个小半年时间了解c和c++,之后你就会觉得看书、看资料可以轻松很多。&/p&&p&&b&如果你是一个上班族,&/b&但是刚刚学习编程,可能学c和c++对你来说有些复杂和困难,因为学习它们确实是很需要时间。你们不像在校生那样有大把的连续时间,而零碎的时间去学习一个比较复杂的东西效果不见得有那么好,所以可以先学一些【更容易见效】的编程语言,&b&从python入手吧&/b&,至少能快速做出一些小应用,不至于丢失了兴趣,但是真的要入门编程又还得看看与计算机系统相关的书籍,这样才能更深层次地去编程,譬如【深入理解计算机系统】这一本书可以读很多遍,这本书把整个计算机系统给串起来了。&/p&&p&&br&&/p&&p&&b&&i&2.学习编程,我需要学习哪些课程?&/i&&/b&&/p&&p&&b&我要学哪些课程?我为什么要学习如高数、离散数学、线性代数、概率论等课程?&/b& &/p&&p&这个问题也是之前困扰了我很久的问题。不过我现在想通了,对于【高数、离散、线性代数、概率论】等课程,很好解释,做算法的同学肯定知道为啥要学习这些课程。&b&机器学习&/b&中会大量用到上述提到的课程,所以会比较好理解。对在校生而言,学校开设的很多课程我们不知道为什么要学,我们很疑惑,不知道学它有什么用,这个时候我们就会很纠结,还会产生抵触情绪。这很正常,因为我们学习得不够深入,自然不能理解它们的用处。&/p&&p&在我看来,&b&大学本科课程更多的是面向“面”的教学&/b&,即什么课程都教给你一些,但是又讲得不那么深入;而&b&工作或者读研,更多的则是面向“点”的学习&/b&,用到的知识更专。本科时,学校也不知道你以后是去搞算法、还是搞架构、还是搞服务器开发,甚至去搞硬件,所以学校需要你学很多课程,至少有个了解。对学生来说,一方面可以从中选择自己感兴趣的点;一方面也可以对未来的就业方向有些启发。所以即使像数电、模电等课程,虽然之后可能用不着,但是你也要学,并且会花费大量的时间。虽然你最后不一定去搞硬件,但是这些课程也会让你更容易去理解一些知识,比如cpu中的逻辑器件。&/p&&p&如果你在大一的时候就有一个明确的定位,知道自己今后想从事哪方面的工作,课程与课程之间是可以调一下&b&优先级&/b&的。不过像大学物理,这种课程确实是对编程没有帮助,但是像我前面所说的,大学教育更注重广度,大物等课程可能就是为了给你普及生活常识吧。&/p&&p&其实,大学教育的问题是普遍存在的,我认为我们学习一项技能的时候,应该采取的是&b&项目驱动式学习&/b&,即需要用到什么东西时不会了再去学,而不是先填鸭式的都填进脑子,并且在学习的过程中我们还不知道它这是干嘛用的,等之后用到了,甚至不记得自己学过,反而查资料才会想起:哦,原来我之前学的xx科目是这个用处啊,可是我当时并没有好好学。很多时候学生时间的浪费可能还是要怪老师、怪学校,他们一开始没给我们做好充分的课程介绍。所以,在经过比较多的编程和项目实践后,我认为一个比较好的学习方式是,&b&改良版的项目驱动学习法&/b&。即:&/p&&p&&i&&b&学习一段时间,做个小项目,将做项目遇到的问题记下来,针对性地学习相关知识,然后再实践,再学一段时间理论,让知识成网状发射状地变大。当然,项目驱动式学习有一个弊端,就是每次学习的知识都是项目所需要的,很零碎、不成体系,所以需要改良,即在采取项目驱动学习法的时候每天抽一段时间去完整地读一本书,或者一个相关问题的完整介绍,这样就很容易把一些知识成体系地串起来。这样一段时间下来,慢慢的,你就知道我们为什么要学那么多科目,学这些科目能干什么。&/b&&/i&&/p&&p&为了表达地更加形象,我就举一个小例子,是我最近遇到的。我本身的工作是做Linux C++的,但不仅限于此。我个人对python、数据分析,以及机器学习等内容比较感兴趣,大家可以看到我最近也在我的专栏发布了很多文章。就从&b&数据获取&/b&开始,我讲讲我这两个月做了什么东西。&/p&&p&谈到数据获取,可能最容易想到的是&b&爬虫&/b&,爬虫是一个在知乎上被说烂了的话题,所以我不想多说它是什么。很多时候有人觉得爬虫简单,为什么呢,因为有&b&现成的框架&/b&,所以获取少量的数据就比较容易。但是当你需要爬取的数据很大的时候(比如我之前抓取了知乎500万用户的数据,在下班的时间、用自己家里普通的pc,计算机性能并不是那么好,比不上服务器,又要在不被封IP的情况下抓到这么大量的数据,然后对数据进行清洗,最后还要可视化展示),使用现成的爬虫框架就并不是那么容易实现了。况且,我需要抓很多数据源,并不是一锤子买卖。所以我选择去&b&开发一个系统&/b&,即在现有的框架下进行二次开发,搭建一个属于自己的爬虫系统,并植入一些算法。我在系统中添加了很多中间件,直到现在,它还可以在10分钟内就部署一个能抓取大量数据的爬虫应用。当然,这个过程也遇到了不少麻烦,我就简单讲讲,怎么去攻克一个个问题。&/p&&p&下面先给出一个&b&树形图&/b&,从上往下每一个圈都代表了学习过程中遇到的难点,如果你现在看不懂,没关系,我想告诉你的是一种梳理知识的方法:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-becccee03724_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&891& data-rawheight=&1465& class=&origin_image zh-lightbox-thumb& width=&891& data-original=&https://pic2.zhimg.com/v2-becccee03724_r.jpg&&&/figure&&p&如上图所示,就是一个项目驱动式学习的例子,我们的目的是为了获取数据,所以选择了爬虫:&/p&&ol&&li&爬虫可以理解为一个简单的过程:发送request,获取response,然后提取数据。这个过程会涉及到网络,是发送http还是https请求;目标网站是否需要登录,是post请求还是get请求,从这条线,衍生出了一条对网络进行学习的路径。&/li&&li&获取到网页之后,如果不是结构化的数据,可能返回的是一个html源代码,那么可能就需要了解dom,或者html页面解析的知识,甚至需要了解一下前端开发。&/li&&li&在抓取的过程中,经常会遇到数据中途不能被爬取的情况,一般是IP被封禁了,那么可能又要用上代理,代理是什么呢?http,https代理能不能混用呢?如何构建一个代理池呢?这里又有很多要学习的东西。还有可能遇到的情况是,抓下来的数据是加密的,需要通过js解密,这时候就要了解一下js,如何用爬虫模拟浏览器进行抓取。除此之外,如果抓取的频率不对,很多数据源会给你假数据,这就是一些经验问题了,本文不是技术文,所以就不多讨论。&/li&&li&当解决了上述问题后,我们好像可以拿到一些数据了,但是当数据大起来,问题又复杂了,你可能需要使用分布式抓取了,这时候你可能需要了解一下redis,当request产生的速度大于其消费的速度之后,你的任务队列可能爆炸,所以这里又涉及到算法和数据结构的应用了。&/li&&li&数据量上去之后,把数据写在文件里面是不靠谱的,这时候又涉及到存储了,到底是使用关系型数据库还是非关系型数据库呢,有什么区别呢?存进去的数据怎么去重呢?为什么insert操作越来越卡了呢?电脑怎么越来越热了呢?索引是什么,什么时候该建立索引呢?这里又牵扯到数据库原理相关的知识。&/li&&li&遇到一些比较难处理的网站,比如有验证码识别该怎么办呢?其实对于很多纯数字和字母的验证码都很好解决,自己用深度学习训练即可。在TensorFlow的Demo中就要生成验证码自己训练的教程,然后制定个中间件放在爬虫系统中,这个问题就解决了。可是什么是深度学习呢?这里又引出一条对深度学习进行探索的例子,而我自己也是之前在学校的时候自学了小半年机器学习,有了一定的基础后,才能比较容易地上手TensorFlow框架。再往下就比较深了。&/li&&/ol&&p&上述六点简单讲了讲项目驱动式学习的介绍,其实,你看到的每一个小圆圈,深挖下去都大有文章。我们现在看到的只是冰山一角,任何一条学习路径学习下去都深无止境,我们不可能完全学会,可是&b&项目驱动式学习最大的好处是让你知道你应该去学习什么,而不是先学一大堆知识,再去做一个项目&/b&。严格来说,项目驱动式学习的可视化路径是一张网,而不是一棵树,这里画成树状只是为了便于大家理解。&/p&&p&除了获得数据,还有清洗数据、分析数据,甚至挖掘数据,最后可视化数据并且展示数据,这里我就不一一介绍了。可以参见下面这张图,&b&如果大家想看我做的一些成品,可以看看我的其他文章&/b&。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-4e82f497fda1c55dc7dec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&682& data-rawheight=&938& class=&origin_image zh-lightbox-thumb& width=&682& data-original=&https://pic3.zhimg.com/v2-4e82f497fda1c55dc7dec_r.jpg&&&/figure&&p&&b&&i&3.学习编程是否需要制定计划?&/i&&/b&&/p&&p&&b&学习编程是否需要制定计划,该制定什么样的计划呢?&/b& &/p&&p&我认为不只是编程需要制定计划,其他任何的学习和工作都需要制定计划。我从13年上大学就开始定期给自己制定计划,这个习惯也一直坚持到了现在,受益匪浅。当然也不只是制定学习计划,还可以列一些自己需要做的其他的事情。我最近在整理笔记的时候也发现了一些之前记录的计划和清单,可以给大家看看。&b&比如下图就是我14年写的笔记,笔记上都留下了最后一次打开的时间&/b&。列举了一些自己需要看的文章,因为当时不太懂得规划,所以比较乱。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-4f616fcac9fdcfb0ab1551b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1292& data-rawheight=&1028& class=&origin_image zh-lightbox-thumb& width=&1292& data-original=&https://pic2.zhimg.com/v2-4f616fcac9fdcfb0ab1551b_r.jpg&&&/figure&&p&到了16年的时候,我做计划做得更加有条理了。下图是16年10月30日的计划,那时候我已经大四了,并且已经找到了工作、签了满意的offer,并且没有什么课,按理说可以放松放松了,不过我还是制定了一些学习计划,并且选择在11月去百度实习。&b&从内容上看,主要是学习英语和计算机专业课,因为大一大二的时候我确实不明白为什么要学习专业课,到了大三下想清楚原因以后,我也就一直在重新学习,因为计算机专业课真的很重要!学好了这些课,能让你在日后的学习工作中轻松不少:&/b&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c2db9c4345cc6caab4f96_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1706& data-rawheight=&1406& class=&origin_image zh-lightbox-thumb& width=&1706& data-original=&https://pic4.zhimg.com/v2-c2db9c4345cc6caab4f96_r.jpg&&&/figure&&p&&b&除了大四制定的计划外,大二的时候我也制定过较为详细的学习计划(如下图),把需要学习的内容进行了编号,存入表格,这样才能让你过得有条不紊。当然,很难完全按照计划去执行,不过制定相应的计划能让你清楚地知道自己应该干什么。&/b&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-1ba136d2ccd82b153fedfaa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1672& data-rawheight=&1266& class=&origin_image zh-lightbox-thumb& width=&1672& data-original=&https://pic2.zhimg.com/v2-1ba136d2ccd82b153fedfaa_r.jpg&&&/figure&&p&所以,如果你是在校生,那么好好制定一个计划吧,因为你有大把的时间。当然,如果你已经毕业了,没关系,我现在也在上班,同样也列举了自己最近要学习的内容,如下图(2月27日更新过),包括了短期和长期需要学习的内容:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-abe41fc9daee9db3ad44_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1542& data-rawheight=&1258& class=&origin_image zh-lightbox-thumb& width=&1542& data-original=&https://pic4.zhimg.com/v2-abe41fc9daee9db3ad44_r.jpg&&&/figure&&p&&b&&i&4.编程是否需要做笔记和写博客?&/i&&/b&&/p&&p&我觉得,写不写博客无所谓,因为博客是要写出来给大家看的,可能要保证格式美观、语法也要尽量准确,最好比较有文采,我觉得太麻烦也就一直没写。&b&而笔记是必须要做的,并且记笔记是一个长期的过程。在学习的过程中,我们一直都在追求一种最高效的学习方法,比如,同一个班的同学,他用他的学习方法考上了清华,而你用同样的方法就不行,为什么?因为他的方法对他自己而言是定制化的,可能且大概率不适合你,比如他的笔记你不一定能看懂,因为他可能设计了一套属于自己的符号。而就编程而言,很多同学说善用搜索引擎,是对的,可是搜索引擎搜出来的是别人的答案。你照搬过来,也许可以用,但是你没有记住,这些知识并不属于你,之后你可能还会遇到同样的问题,又要再搜索一遍,可能很难找到之前的那个答案了。但是记笔记就不一样,记笔记是定制化的,对你自己定制,你可以用自己最爽的表达方式来描述一个问题,是自己写给自己看的东西,看了几遍之后就能非常迅速和容易地理解。之后遇到相同的问题可以快速地通过找笔记解决。&/b&&/p&&p&举个例子,下图是我记录的一些关于gdb【linux下调试c++的工具】的使用的一些笔记。我只记录了我自己最常用的一些内容,也许你看着很乱,但是我就能很容易看懂,这就是我的定制化。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-6b9bdba6bb42c72f4d8cc9_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2268& data-rawheight=&1546& class=&origin_image zh-lightbox-thumb& width=&2268& data-original=&https://pic3.zhimg.com/v2-6b9bdba6bb42c72f4d8cc9_r.jpg&&&/figure&&p&记笔记的习惯一定要坚持,等过个一年或者两年,这就是你巨大的财富,因为那是只有你才能看懂的东西。我已经记录了4年多、1G多的内容,现在的笔记基本已经形成了体系,可以给大家展示其中的一部分。&/p&&p&专业知识相关笔记:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-1a92b35caf6c7daa7fce92d8b37ba5b4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&559& data-rawheight=&817& class=&origin_image zh-lightbox-thumb& width=&559& data-original=&https://pic3.zhimg.com/v2-1a92b35caf6c7daa7fce92d8b37ba5b4_r.jpg&&&/figure&&p&开发相关的笔记:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-e47df51a4ad6c7f7cb239a1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&559& data-rawheight=&924& class=&origin_image zh-lightbox-thumb& width=&559& data-original=&https://pic3.zhimg.com/v2-e47df51a4ad6c7f7cb239a1_r.jpg&&&/figure&&p&一些类目:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ac61d7e115b0f3bdbae27c390d923141_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&239& data-rawheight=&991& class=&content_image& width=&239&&&/figure&&p&&i&&b&5.有什么比较好的编程方法?&/b&&/i&&/p&&p&除了上述分享的一些方法,我认为在同一时间段不要学习太多类别的课程,比如你可以同时学习python和html/css,但是你不要同时学python、操作系统、编译原理、计算机组成、数据结构、网络,我曾经试过,一门课没学一会儿就学下一门,其实上一门根本学不到什么实际的知识。因为记忆知识是符合&b&艾宾浩斯记忆曲线&/b&的。对于一门课,特别是很难的专业课,譬如操作系统,你每天看半小时,效果是比较差的,可能你热身就得半小时。所以宁可每天学两门,然后每一门学长一点的时间,比如两小时。【毕竟学校上课,一次课也得两小时】,要避免贪多,一口吃不成个胖子。 &/p&&p&&br&&/p&&p&&i&&b&6.我需要刷oj么?&/b&&/i&&/p&&p&我认为刚开始编程的时候还是应该刷的,但是一定要注意,不要被你周围的“X神”给误导了。因为我上大学的时候,身边总是有很多搞计算机竞赛的人,他们之间都互相称对方为“X神”,某某神又使用一个牛逼的算法,将程序时间从1秒降低到了0.999秒。我要劝大家的是,刷题不是为了达到这个目的,不是说非要在竞赛中拿奖,除非你是特别喜欢,否则,没必要去&b&背代码&/b&。我们刷题的目的是适应写代码的感觉,在这个过程中你会遇到编译错误,你会慢慢去记住一些语法、关键字,并理解一些概念,还可以自己去使用它,比如实现数据结构。慢慢的你就会变得有经验,知道一些错误产生的原因。我也是慢慢这样过来的,我现在在工作和下班以后写代码时,基本都不用IDE了,比如写c++,要么vim,要么就是sublime,而调试用的是我前面提到的工具gdb。即,有一个文本编辑器就能写代码,脱离了IDE的束缚。在写oj之后一段时间,在比较熟练了之后,就可以不去刷题了,可以去譬如github这样的网站上找点项目来看,然后自己跟着写一下,编程能力慢慢就提升了。就计算机专业来说,很多同学在大一上完编程课之后,就很少写代码了,这样是很不好的。刷题除了可以锻炼编程能力,对于找工作前突击也很有作用。比如,我之前投递过华为公司的研发岗位,校招的时候有笔试题。我就在16年国庆的时候刷了一下华为的oj,我记得笔试是600分的总分,过100就给面试机会,而我很轻松的就拿了500分,而当时也就刷了20多道华为的题。 &/p&&figure&&img src=&https://pic3.zhimg.com/v2-3d5b9bcf85aeb841a9537_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&674& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic3.zhimg.com/v2-3d5b9bcf85aeb841a9537_r.jpg&&&/figure&&p&&i&&b&7.看书还是看视频?&/b&&/i&&/p&&p&网上有不少人鄙视看视频学习的同学,我不知道为什么,因为我认为看视频是一个很好的学习方式。不过我们得明白看书和看视频分别有什么优缺点。&/p&&p&其实我是很建议看视频入门的,因为目前网上的应用型【非学术型:比如清华大学的操作系统,非常难】的视频都是很简单的,很多是面向初学者的,视频能用较短的时间告诉你你现在所学的技术可以干什么,可能需要先修哪些知识,可以帮助我们搭建一个项目驱动式学习的网络。可是视频也有个缺点:就是知识非常的杂,很不系统。虽然现在很多教学网站都提供了学习路径,但是这些路径中的视频很多时候都不是同一个老师录制的,只是按照知识的依赖关系排的顺序,所以,如果想通过视频去系统地学习一门知识,是比较困难的。【当然,一些学术型的视频还是很推荐的,比如斯坦福的机器学习,清华的操作系统、数据结构等课程,能坚持看完,绝对受益匪浅】。而应用型的,比如web开发等知识,还是得看书。书籍等特点就是系统化,由浅入深,你可以定制化地看自己薄弱的章节。所以一个比较好的学习方式是:&/p&&p&&i&&b&看视频入门,看书进阶。&/b&&/i&&/p&&p&&br&&/p&&p&&i&&b&8.多久能学会编程?&/b&&/i&&/p&&p&其实这个问题是没有答案的,如果只是想做出一个小应用,2个月足矣,而就我个人而言,我认为学习编程不是学习一种编程语言,而是学习一个生态,一个计算机系统,所以无止境。&/p&&p&&br&&/p&&p&&i&&b&9.我应该选择什么资料,看什么书?&/b&&/i&&/p&&p&其实这个问题也是很多编程新手容易困惑的问题。网络上拥有我们一辈子都看不完的教程和资料,所以现在应该不会存在找不着视频教程、找不着书看的问题。而问题就是我们不知道看什么视频、看什么书。从开始学编程到现在,我也买了上百本书,而真正适合自己的好书并不多。而视频教程的问题就更严重了,东看一点、西看一点,知识很难组织成网络。所以&b&学习编程的过程中,我们遇到的最大的问题是:当我们遇到问题的时候,在大量资料面前,我们不知道选择什么资料去学习。&/b&即使我们使用项目驱动式学习的方法找到了我们的方向,但是同一个路径下,也有很多资料。前文列举的项目驱动式学习的图中,我们是自上而下的去发现问题,然后再解决问题。如果能有人帮我们组织好学习路径,然后自下而上地去学习,那么效率可能会提高很多。&/p&&p&&i&&b&不过不用担心,我已经尽我所能,将我看过或者我认为好的课程和书本资料给串了串,整理好上传了,所有学习资料均免费,无任何收费课程。&/b&&/i&&/p&&p&&i&&b&如果需要,请关注微信公众号【大数据前沿】回复:【编程路径】获取。&/b&&/i& &/p&&p&&b&如果觉得有帮助,请直接赞赏!!!&/b&&/p&&p&顺便插播个广告:&/p&&p&【大数据前沿】:用键盘舞动青春,用数据描绘世界。传播IT技术,发现编程之美。了解科技前沿,挖掘数据价值。人生苦短,我用python,编程路上,有我陪你。如果你想和我一起学习编程,就用微信搜索:【大数据前沿】关注吧,&b&对于部分技术性文章,我会录制视频教程分享实现过程,有时候也会留下数据和代码让读者练习&/b&。让我们一起进步吧!&/p&&p&近期热文:&/p&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic2.zhimg.com/v2-ebcfa76c746dd224f7af543bdx120.jpg& data-image-width=&400& data-image-height=&240& class=&internal&&二胖:大数据解密之你的同事都跳槽到了哪些公司&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic3.zhimg.com/v2-9d62b11bdce05fc0d35a_180x120.jpg& data-image-width=&400& data-image-height=&245& class=&internal&&二胖:30岁,大学毕业的你,月薪多少?&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-369ec090d7aed9cc4a017_180x120.jpg& data-image-width=&500& data-image-height=&350& class=&internal&&二胖:大数据告诉你旅行青蛙饲养员的秘密&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic3.zhimg.com/v2-959b7bda8be9dc83abd2_180x120.jpg& data-image-width=&1126& data-image-height=&630& class=&internal&&二胖:用 python 挖一挖成都房价&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic2.zhimg.com/v2-fad3b31d94bbef0f48c29_180x120.jpg& data-image-width=&971& data-image-height=&703& class=&internal&&二胖:不会爬虫怎么获取数据?&/a&&p&End&/p&&figure&&img src=&https://pic4.zhimg.com/v2-0e5bb3350f3fdd1afd8c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&720& data-rawheight=&466& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic4.zhimg.com/v2-0e5bb3350f3fdd1afd8c_r.jpg&&&/figure&&p&&/p&
从大一入学被调剂到计算机专业,到喜欢上这个专业,再到毕业拿到10多个offer,最终进入理想的大厂工作。回想起来这些年确确实实踩了很多坑。我刚开始学习编程的时候也想一口吃成一个胖子,想速成,但是有时候却是不尽人意。回忆了下这几年学习编程的过程,…
&p&&b&量子的性质恰恰说明我们很有可能在虚拟现实当中。&/b&&/p&&p&&br&&/p&&p&首先&/p&&p&&b&能量的交互存在最小实体:量子&/b&,这个现象就很奇怪。&/p&&p&凭直觉,我们会认为自然界的物质是连续的,就像数字0和1之间可以塞进无穷多个0点几的小数。&/p&&p&而实际上,能量的传递却是间断的,是一份一份的量子构成的。量子化假说意味着物质或能量的大小可以由一个量子的整数倍表示。&/p&&p&为什不能把物质或能量细分成无穷小呢?&/p&&p&如果从游戏开发程序员的角度想就会很容易理解了。&/p&&p&一个游戏开发程序员,出于对内存容量,CPU的运算能力的考虑,不会把游戏画面无限细化,只要满足玩家可接受的清晰度就可以了。 所以2D游戏中会出现最小显示单位:像素pixel,3D场景会有最小单位:体素voxel。&/p&&p&如果上帝他老人家设计宇宙这个大游戏,他也会偷懒只把游戏分辨率细化到一个游戏玩家察觉不到的级别吧…… 反正人肉眼凡胎,看不到那么细,不会觉得这个游戏不真实……&/p&&p&可人类通过科学技术手段可以看到量子级别的现象。而且发现了一连串诡异的,不符合常理的性质:量子纠缠,量子的真随机,机波粒二象性,观察者效应等等……&/p&&p&这些看似奇怪的现象,如果站在游戏开发程序员的角度来看,都可以解释的通。&/p&&p&&br&&/p&&p&&b&量子纠缠&/b&&/p&&p&处于纠缠态的两个粒子,即使相距甚远,也可以瞬间感应,一个粒子的状态可以瞬间反映到另外一个粒子上。&a href=&//link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%25E9%E5%25AD%%25BA%258F%25E7%25B5%2590%23cite_note-6& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&最近完成的实验显示&/a&,量子纠缠的&b&作用速度&/b&至少比光速快10,000倍。这还只是速度下限。根据量子理论,测量的效应具有瞬时性质,不耗时间。&/p&&p&互相纠缠的量子之间是如何实现这种超距离的瞬时作用呢?&/p&&p&程序设计的角度很容易实现:&b&使两个相互纠缠的粒子的指针指向同一个内存地址&/b&,这样即使他们之间相隔万里,他们的信息也是共享的。改变了一个粒子的状态,就等于另外一个粒子也同时做出了相应的改变。&/p&&p&另外注意,&b&量子纠缠不能用来传递信息&/b&,因为量子状态只能被被动测量,无法显式更改。 这一点可以理解为&b&纠缠量子所指向的内存块是只读的(read-only),&/b&上帝未对我们开放写权限。&/p&&p&&br&&/p&&p&&b&观察者效应&/b&&/p&&p&电子的双缝干涉实验证明:有观察者和无观察者时,物质的形态是有差异的。&/p&&p&无观察者时,电子通过双缝,出现干涉图样,这是波的特性。 有观察者时,不会出现干涉图样,显示出粒子的性质。电子仿佛知道了自己被观察了,乖乖地做回了粒子。&/p&&p&这简直毁三观有木有!!! &/p&&p&不过从程序设计角度也很好理解:&/p&&p&宇宙在设计时,为了节省计算量,所以粒子按照波的方式进行计算,而当玩家观察某个物体时,其按照更精确,也更耗费CPU的粒子方式进行运行。&/p&&p&要知道,按波和粒子的方式运算所消耗的CPU和内存资源的差距是很大的。 按波的方式处理,只需把物质总体带入到波的公式里即可,比较容易计算。 按粒子方式来处理,需要为每个粒子分配一个单独的线程去处理这个粒子的运动,耗时耗资源。 这也是为什么以现代计算机,依据&b&分子动力学&/b&(molecular dynamics),莫说是一个宇宙,就连一个蛋白质分子的自动折叠都很难实现模拟。&/p&&p&&br&&/p&&p&&b&量子的“真随机“?&/b&&/p&&p&量子体现一种真正的随机性。 量子的状态不受任何条件制约,毫无运动规律可言,无法预测其结果。&/p&&p&这与一般的因果论相违背。宇宙所有事物都有其运动规律,所有事件都能追溯到其原因,如果知道某一时刻宇宙中所有粒子的状态,应该可以预测下一时刻宇宙的样子的。 这就是为什么爱因斯坦说,上帝不掷骰子。&/p&&p&量子幽灵般的状态,似乎打破了宇宙万物皆有规律的一般认识。&/p&&p&然而如果宇宙是建立在一个虚拟机上的话,量子的随机性就好理解了: 宇宙设计者用&b&随机数生成函数&/b&生成&b&统计意义上的随机状态&/b&,赋值给量子。同时让随机函数对虚拟机(宇宙)内部是不可见的。宇宙中可见的只有量子的随机态。&b&虽然这种随机是统计上的伪随机,但是由于宇宙内部无法追溯量子状态的原因,导致在人类看来量子就是真随机&/b&。&/p&&p&至于如何用程序如何生成统计意义上的随机数,可以参照这个问题:&a href=&https://www.zhihu.com/question//answer/& class=&internal&&电脑取随机数是什么原理,是真正的随机数吗?&/a&&/p&&p&&br&&/p&&p&补充, 对于相对论提到了一些现象: 光速不变,时间膨胀等原因, 给出程序员的猜测&/p&&p&&b&光速不变&/b&&/p&&p&光在真空中的速度约等于m/s&/p&&p&各种实验证明,这是宇宙中的极限速度,不能再快了。 &/p&&p&为什么不能无限快下去呢?&/p&&p&程序员是这么想的:&/p&&p&光子的速度是情报处理的产物,也就是说,以一定的频率,我们的世界得到更新。比如说现在的主流CPU的时钟频率在大概在3GHz左右, 也就是每秒刷新30亿次。同样的,如果宇宙后台的处理器如果有固定的时钟频率的话, 可不可以理解为光速是当前宇宙处理器的时钟频率所能支持的最大移动速度呢?&/p&&p&&b&时间膨胀&/b&&/p&&p&相对论说:相对于静止的物体,运动的物体的时间会过得慢。即时间膨胀现象。&/p&&p&那么为什么速度增加会导致时间变慢呢?&/p&&p&程序员的 idea:&/p&&p&喜欢玩游戏的人,应该有这样的体验,计算机的处理速度降低的时候,游戏也会出现卡顿,游戏里的时间也会变慢。同样,在我们的世界,移动速度的提高,会导致所需要处理的数据量增大,从而增加了处理器的负荷, 导致时间变慢。&/p&&hr&&p&最后&/p&&p&联想到了电影《&b&&a href=&//link.zhihu.com/?target=https%3A//movie.douban.com/subject/1300282/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&异次元骇客&/a&&/b&》的情节:有一天,一个活在虚拟世界的人对自己世界的真实性产生了怀疑,于是他开着车,朝着一个方向,不停地开,不停地开,不停地开,。。。。,后来他开到了蛮荒之地,看到了这个 ?,吓尿了。。。。&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-de26df61efecf8ccace69358_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&450& data-rawheight=&376& class=&origin_image zh-lightbox-thumb& width=&450& data-original=&https://pic3.zhimg.com/50/v2-de26df61efecf8ccace69358_r.jpg&&&/figure&&p&同样的,在我们的世界,不久之前,一帮物理学家:爱因斯坦,波尔,普朗克,。。。。也开着车,朝着微观世界不停地开。。。,后来他们看到了量子。。。。&/p&
量子的性质恰恰说明我们很有可能在虚拟现实当中。 首先能量的交互存在最小实体:量子,这个现象就很奇怪。凭直觉,我们会认为自然界的物质是连续的,就像数字0和1之间可以塞进无穷多个0点几的小数。而实际上,能量的传递却是间断的,是一份一份的量子构成的…
&p&记得有本书叫《未来简史》,里面提及了这种可能性,大意是:&/p&&p&当科技发展到极高水平时,做出《西部世界》、《黑客帝国》那样的虚拟世界,绝对是可行的(如果你觉得不可行,那么请以3000年前的古人的角度来审视一下当今的网游、手游、VR游戏)。&/p&&p&而当虚拟世界里的文明发展到极高科技水平时,他们还会再创造更深层的虚拟世界。&/p&&p&也就是说,虚拟世界是可能有无限多个的,但真实世界肯定只有一个(不考虑平行世界),所以我们处于真实世界的可能性无限趋近于0。&/p&&p&另一方面,量子力学在微观世界中颠覆了经典力学的定律,量子力学所依托并进行过验证的假设——物体只有被观测时才塌缩成确切状态,否则会处于混沌叠加状态,像是一种节约运算资源的优化方案,类似如电子游戏中只会渲染用户眼前所能看到的画面,以及网络游戏只在有玩家的地图区域才刷怪。&/p&&p&再有就是,现代生物学更倾向于认为人并没有所谓的自主意志,人其实是依靠生物算法运作的一台生化机器,人的基因、五感、记忆、体内激素水平等等属性构成了私有变量和输入参数等数据,人的所有行为决策无非是通过这些数据来计算出的结果,所谓的自主意志其实仅仅是大脑活动的映像感受,大脑不运算时就不存在任何意识了,比如当进入无梦睡眠或死亡状态时,自主意志或者说灵魂何在?离开了前述的输入数据,或是离开大脑这枚处理器兼存储器,自主意志或灵魂又能做什么?&/p&&p&对一个人来说,他喜欢吃什么、喜欢干什么、喜欢谁、如何处理问题、以怎样的口吻对话、如何思考……这些特点构成了他独特的人格,但是这些特点基本都离不开基因、五感、记忆、体内激素水平等等因素,甚至一些药物、病毒、细菌构成的因素,随意调整这些因素就会导致人格巨变,那么其自主意志到底是在哪里发挥着作用?&/p&&p&也就是说人类所谓的自主意志,并不是人类灵魂涌现的意志,其实只是复杂的条件反射,类似于现在计算机领域的人工神经网络技术——输入参数&进行运算&输出参数,不断训练修正。&/p&&p&或许你会觉得这纯属无稽之谈,因为你确实能感受到自己的意志非常自由,未受拘束。&/p&&p&但假设有个胆小的人,仅用肾上腺素甚至酒精就让他做出了事后令其自己都惊讶的事,而其做事时也觉得出于本意呢,难道酒精可以扭曲灵魂吗?&/p&&p&在我们地球上,有一种寄生蜂,他会将一种毛虫作为宿主产卵,宿主不仅不会排斥,甚至还会至死捍卫寄生蜂卵中孵化出来的幼虫,并最终甘愿作为幼虫的食物。&/p&&p&还有一些实验,针对部分癫痫患者,他们经过手术切断了两个脑半球的神经纤维连接,他们两边的半脑可以相对良好地独立运作,并且各有各的想法,一个男孩被问及“未来想当什么?”时,左脑回答“绘图员”,右脑回答“赛车手”,那么其灵魂到底藏在哪半个脑子里?(对这种左右脑分离的研究实验感兴趣的朋友可参看答案底部的延展阅读部分)可以看出自主意志并不存在,两边大脑会对一个问题给出不同的答案,这很明显是两边的数据和算法差异造成的不同结果输出,没有了中间的数据连接,也就少去了最后的甄选步骤,直接把两个结果都输出了,就好像计算机编程中去掉了最后的二选一输出函数。&/p&&p&如果你还是纠结于自主意志是否存在的问题,但也认为所谓的自主意志,是存于大脑之内的,且会随着大脑的死亡而死亡,那么就应该认定它就是大脑的一部分功能而已,并不会超然于物质乃至次元,因为这里我倒是觉得挺适合应用一下奥卡姆剃刀原则——“如无必要,勿增实体”,即如果说有这么一个看不见、摸不着、无法证明的东西,必须依附于大脑存在,那么不如直接否认这个东西的存在。&/p&&p&&br&&/p&&p&总的来说,如果上面的设想是正确的,那么也就是说绝大多数人只是NPC而已,人生赢家之类的成就基本就不用太惦记了,真的就是“得之吾幸,失之吾命”,能活得风生水起的大都是《西部世界》里那种RMB(USD)玩家。&/p&&p&&br&&/p&&p&如果断言我们所处的就是虚拟世界的话,有些问题似乎也就更好解释(歪解)了:&/p&&p&时间为什么不逆流呢?因为数据总是在不断修改和覆写的,回档只能依靠快照备份,或者从最初开始重新演算所有修改直至目标的回档时间点,并不能线性回流。而如果你很皮地实现了超光速,就会导致宕机回档到最近的快照。&/p&&p&那为什么接近光速运动时,时间会变慢呢?因为虽然承载我们这个虚拟世界的计算机运算速度很快,但是高频运算也是会增加其运算负荷的,尤其多线程程序,比较容易达到线程的瓶颈,但是这种时间差异对于这个虚拟世界的拥有者来说不值一提,所以没有怎么调整,想象一下“天上一日,地上十年”的概念,我们的世界流转几亿年,也许宿主世界只是过了几小时。&/p&&p&为什么无法穿越到平行世界,但是可以通过量子力学借用平行世界的运算能力呢?因为承载虚拟世界的计算机是集群运算的,就像这个云那个云一样,所以运算能力是共享的,但是为了避免玩家太多太乱,并且期待得到多种可能的宇宙发展结果,还是会划分服务器,也就是平行世界,众所周知,转服是没那么容易的,尤其是测试服。&/p&&p&宿命论会是真理吗?有一种想法是这样的,无论一维、二维、三维世界,乃至我们所处的四维世界,全部都是固化的,你可以把四维世界想象为一部三维动画片,时间轴就是第四维度,在其中一个“四维帧”里面,就是一个静止的三维世界。任何维度的事物,想在维度内主动或被动改变自身是不可能的,只有更高维度的事物才能在延展出的空间内对其进行延续性变化,这也无法修改前面“帧”里的事物,所以我们所处的四维世界(包括历史和未来的所有“四维帧”),只会被五维世界或更高维度世界里的事物更改,且不会影响到这一“五维帧”内静止的我们。即过去和未来一样,都是恒定不可变的,如果你能有幸窥探到未来发生的事,那么其就一定会发生,你就算有意识地规避和改变也毫无作用。那么如果说我们的世界是虚拟世界的话,我们知道计算机生成的随机数被称为伪随机数,即并不是真正的自然随机化,那么这个虚拟世界的发展也就注定会只有一种可能性,无论重新运行多少次都会是同一结果,也就再一次佐证了宿命论的说法,但是呢,如果这个虚拟世界里有来自宿主世界的玩家或GM干扰的话,他们就会彻底打破我们固化的未来,这和多维世界的设想不同。&/p&&p&为什么地球成功孕育出人类这么低概率的事件还是切实发生了呢?以前我们只能用人择原理予以牵强地解释,现在我们可以热血澎湃地说:这是剧情安排!&/p&&p&——————————&/p&&p&注:&/p&&p&这里所说的虚拟世界,并不一定要完全复刻真实世界的细节和法则,虚拟世界可能也极可能与真实世界存有差异,比如网络游戏这种最简陋的虚拟世界也算是虚拟世界的,其与真实世界差异巨大。我们可以假设真实世界里一切都是符合经典物理学的,而虚拟世界为了节约运算资源,采用量子力学方式进行优化,也就是只有观测时才确定物体形态,那么虚拟世界里的人利用这个差异(或者说BUG)来造出原子弹什么的,继而与真实世界有了完全不同的科技发展走向,也是很合理且有趣的——至少在管理员发现问题并停机之前。&/p&&p&我认为大脑只是一种物质,并没有灵魂寄宿在里面,只是其有复杂的神经网络结构,才能执行复杂的运算任务,究其本质原理,与计算机甚至普通机械没有根本性区别。我们知道高智能的碳基生物会有自我认知,没理由妄断高智能的硅基物就没有生命和自我认知,或许我们本身就是硅基生物,因为我们是硅计算机所制造的虚拟世界中产生的智能生物,只是在虚拟世界里我们自以为是碳基的。&/p&&p&&br&&/p&&p&延展阅读:&/p&&a href=&https://www.zhihu.com/question/& data-draft-node=&block& data-draft-type=&link-card& class=&internal&&切断人体左右脑的连接会对意识造成什么影响?&/a&
记得有本书叫《未来简史》,里面提及了这种可能性,大意是:当科技发展到极高水平时,做出《西部世界》、《黑客帝国》那样的虚拟世界,绝对是可行的(如果你觉得不可行,那么请以3000年前的古人的角度来审视一下当今的网游、手游、VR游戏)。而当虚拟世界里…
&p&&b&目标:用破铜烂铁打造一个云音乐播放器(长文多图预警)&/b&&/p&&blockquote&&b&作者:传闻中的女黑客&/b&&/blockquote&&p&在啰嗦一堆话之前,我们先来看看这个云音乐播放器是怎么样的:&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-71b3e4c1f1e5fdc4a187e9dc6f320797_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic1.zhimg.com/50/v2-71b3e4c1f1e5fdc4a187e9dc6f320797_r.jpg&&&/figure&&p&你可以在B站看到这个设备的演示视频&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.bilibili.com/video/av/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&bilibili.com/video/av17&/span&&span class=&invisible&&719242/&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&br&&/p&&p&或者在闲鱼直接买下来(假如还没卖出去的话)&/p&&p&&a href=&//link.zhihu.com/?target=https%3A//g.alicdn.com/idleFish-F2e/app-basic/item.html%3Fitemid%3D%26ut_sk%3D1.WFSNk8%252B5oW4DAKHgpeCbNSy2_.QQ.detail..& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&https://g.alicdn.com/idleFish-F2e/app-basic/item.html?itemid=&ut_sk=1.WFSNk8%2B5oW4DAKHgpeCbNSy2_.QQ.detail..&/a&&/p&&p&当然,”废铜烂铁”当然不是真正的废铜烂铁,不过东西基本都是压箱底的玩意,东西是很简陋的,乃至于那几条杜邦线都是翻箱倒柜找出来的,但麻雀虽小五脏俱全,这玩意是一个实打实的网络云播放器,所有的音乐资源都存储在远端,,除了能够装逼外,除了你真的买不起一个手机,除非你真是只能掏出几十块钱的穷逼还想听在线音乐,除非你想在美好的大学本科四年(研究生有点难)的毕业季最后搞一把校优毕业设计,那么,你的机会来了!&/p&&p&&br&&/p&&p&&b&我们先计算一下成本:&/b&&/p&&p&1.VS1053b模块,这个价格为55元,当然,我最开始用的是VS1003b模块,这个只要20块钱还包邮,不用担心,不管是连线还是驱动程序,都能够100%兼容,你把这玩意换VS1003b没一点问题&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-79d99e7e5a5a2bdbe04826_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/50/v2-79d99e7e5a5a2bdbe04826_r.jpg&&&/figure&&p&&br&&/p&&p&&b&2.STM32F407ZG最小系统&/b&&/p&&p&这玩意估计是最贵的配件了,要58块钱,几个需要关照的参数比如工作频率168M HZ,192+4k SRAM,3个SPI接口,总的来说,这玩意用在云音乐播放器上算是奢侈了,不过我压箱底的就只有这玩意,当然你完全可以用更廉价的STM32F103VET6最小系统,这玩意只要36块钱,当然还有更乞丐的F10x系列,估计30块就够了,如果你自己动手焊接的话,也许12块钱就够了,当然因为我们秉承着最快最简单成本最低的装好逼,因此在成本最低和最简单上我们不得不做出一些艰难的决定,自己焊接这种事情除非你是老手,不然还是直接买来得快&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-db0fe2de71c8ed3b3cf238b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic4.zhimg.com/50/v2-db0fe2de71c8ed3b3cf238b_r.jpg&&&/figure&&p&&br&&/p&&p&&br&&/p&&p&&b&3.W5500网络模块&/b&&/p&&p&这玩意很便宜,20块钱左右就能拿下,而且驱动编写简单无脑,IO快速延迟低,避免了直接手撸TCP/IP 协议的一堆问题,我实在想不出为什么不买这玩意.&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-e9f09fc29c5ee6cbfa73b046af686f4f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic4.zhimg.com/50/v2-e9f09fc29c5ee6cbfa73b046af686f4f_r.jpg&&&/figure&&p&&br&&/p&&p&&b&4.耳机&/b&&/p&&p&这个耳机十元包邮,某宝上很多,我就不多说了免得说我打广告.&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-a94fa658c4b23a50a4a05fc04d554767_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1005& data-rawheight=&552& class=&origin_image zh-lightbox-thumb& width=&1005& data-original=&https://pic2.zhimg.com/50/v2-a94fa658c4b23a50a4a05fc04d554767_r.jpg&&&/figure&&p&&br&&/p&&p&&br&&/p&&p&因此在动工之前,你可以先算一笔账,如何做好这玩意&/p&&p&我大致列出了一个价格清单之百元打造云播放器&/p&&p&&br&&/p&&p&&b&土豪版本&/b&&/p&&p&STM32F407ZGT6最小系统58元+VS1053b模块55元+W5500模块20元+稍微能听的耳机一个50元+杜邦线洞洞板排针若干5元=188元!这价格估计能买个不错的MP3了,但是,买的东西不装逼啊,何况我们的还是云播放器呢.&/p&&p&&br&&/p&&p&&b&平民版本&/b&&/p&&p&STM32F103VET6最小系统36元+VS1003b模块20元+W5500模块20元+耳机10元+杜邦线洞洞板排针若干5元=91元,你没看错,91元就能让你舒舒服服装个逼,不管是大学里还是高中里,都可以让你的逼格快速提升一个级别,开不开心?&/p&&p&&br&&/p&&p&&b&乞丐版本&/b&&/p&&p&自己焊接的STM32F103VET6最小系统12元,那么你就在平民版本能省下24元,耳机可以路边摊5元的,那么就省下了29元,那么最后你只需要花62元就能以高逼格形态装上一逼.&/p&&p&&br&&/p&&p&硬件的东西基本上就是花花钱接接线的事情了,剩下就是软件的问题了,要打造这个简陋版本的云播放器,你需要完成以下程序的开发&/p&&p&1. STM32Fxxx一些初始化&/p&&p&2. W5500的驱动程序&/p&&p&3. VS10xx的驱动程序&/p&&p&4. 云音乐播放器的Server端&/p&&p&5. 驱动逻辑调度器&/p&&p&当然,开发环境一般使用keil+Visual studio来完成&/p&&p&如果你搞不懂上面的都是上面都是些什么玩意,没关系,这些我都写好了,你可以直接将附件中的源代码哪来编译然后烧录到芯片中.如果你想进入壕无人性的快捷装逼模式,笔者非常愿意将手上的现成品卖给你,除了不包邮之外,另外再收一丢丢的技术支持费用,没错,你没看错,笔者现成可用的成品+源码只需要1024元就能搞到手,让你愉快装个逼.(论文及技术支持费用另算)&/p&&p&&br&&/p&&p&&b&从这里开始&/b&凡事都有个开始,因此在开始的第一件事情首先是理清我们要面对的是什么,简单来说是一个单片机网络音乐播放器.但我们也不得不面对一些”破铜烂铁”所带来的诸多问题,例如片上RAM只有可怜的192KB,因此不可能和PC端开发APP一样奢侈地开上几十兆的内存做缓存,另外168MHZ这一并不算高的主频也对我们的编码提出了挑战,我们不仅仅要合理分配好处理网络与音频的资源分配问题,内部总线的速率乃至于音乐播放的比特率都应该在严格的计算后进行处理,最后开发也是个问题,即使是C语言也并没有提供完整的标准库供你调用,因此很遗憾的是你不能使用一堆别人已经为你铺好的一堆路,这意味着很多时候你必须”造轮子”,但幸运的是笔者就是一个宁愿栽在自己手里也不想在别人的库中处处躲藏着一堆坑而死的不明不白,另外,VS1053的硬件码与W5500自带的Socket也帮我们省下了一大堆解码与协议上的问题.&/p&&p&现在,如果和笔者一样有造轮子强迫症,那么,这篇文章接下来的内容,就是讨论如何造轮子的.&/p&&p&在动工之前,一些必备的开发及调试环境也是必须的,在本章内容中,使用的开发环境为keil,笔者使用的版本为5.11.0,作为钦定的嵌入式开发IDE,有着对大部分主流芯片的支持 ,自动加入startup(类似于bootloader)代码也是&/p&&p&一大亮点,当然keil还有着屎一样的代码编辑体验绝对让你印象深刻.&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-6823aa49bdfe0b9287292_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&536& data-rawheight=&579& class=&origin_image zh-lightbox-thumb& width=&536& data-original=&https://pic3.zhimg.com/50/v2-6823aa49bdfe0b9287292_r.jpg&&&/figure&&p&&br&&/p&&p&但不管怎么说,嵌入式开发八九不离十需要这个软件的支撑,集成好的环境总比你自己捣鼓些什么gcc交叉编译快得多,又没人看你编译方案多牛逼,为什么不挑选更方便快捷的方案呢?&/p&&p&然后是visual studio这个号称宇宙最强的IDE,虽然在哪个IDE好用上可以说是各有说辞,但按笔者多年的使用经验来说,Visual studio的用户体验几乎可以用无出其右来形容,搭配visual assist X更是让码起来的感觉就像吸了毒,难怪不少码畜码起来废寝忘食,无需怀疑,visual studio的用户体验是毋庸置疑的.&/p&&p&在本项目中,我们将使用Visual studio 编辑器用于编辑单片机与音乐服务端的代码,如果你不熟悉visual studio 我建议你需要稍微花点时间对其界面及操作学习.&/p&&p&另外虽然所有的源代码在附件中都可以直接编译执行或者直接烧录到芯片当中,但是笔者仍然建议读者购买JLINK或者ST-Link等调试器并自己着手修改代码来完成自己所需要的功能.&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-f0a6c3c67eb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/50/v2-f0a6c3c67eb_r.jpg&&&/figure&&p&&br&&/p&&p&最后,本文假设读者熟悉C语言,对数电模电与TCP/UDP网络通讯有基础了解,同时熟悉keil及visual studio的基本操作.&/p&&p&&br&&/p&&p&&b&内部总线协议的选择&/b&&/p&&p&其实本文标题起的并不怎么准确,其实不是选择,而是datasheet已经决定了,就用SPI总线来做模块间的通讯协议,简单来说就是SPI是钦定的,你没得选,但写这篇文章的目的,更多是为什么选择SPI协议而不使用别的.&/p&&p&首先是最直观的一点,不管是W5500还是VS1053模块都支持SPI协议,并且STM32F407ZGT6有三个SPI接口有良好的硬件加速,同时STLibrary(ST公司提供给STM系列的库)中有着对SPI的封装库,这意味着我们能不用花太多功夫在底层通讯协议上.&/p&&p&其次是SPI协议用到的引脚只有四根,连线简单,而且在速度上相对于串口这种龟速接口,至少能达到我们的要求,即便不使用DMA,也不会对效率上产生难以负担的问题.&/p&&p&因此内部总线的协议我们就选用SPI,当然本篇文章并不是专门给读者介绍SPI协议的,但列出一些关键的要点,让不了解SPI协议的朋友涨涨姿势,传授点人生经验,同时说明下时钟对SPI速率的影响好让他跑的比某地记者还快,那肯定是最吼的.&/p&&p&首先是SPI的连线方式,用一张图来概括,那就是&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-45a74aefde5c3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&347& data-rawheight=&148& class=&content_image& width=&347&&&/figure&&figure&&img src=&https://pic3.zhimg.com/50/v2-6d4c8280cebb54081fdc438f60ca3fb7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic3.zhimg.com/50/v2-6d4c8280cebb54081fdc438f60ca3fb7_r.jpg&&&/figure&&p&在模块上这些引脚都有进行标注&/p&&p&&br&&/p&&p&(W5500模块的引脚标注,SCS表示SS)&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-facf827ccf6983529ccbd5e7ba9cf834_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic3.zhimg.com/50/v2-facf827ccf6983529ccbd5e7ba9cf834_r.jpg&&&/figure&&p&&br&&/p&&p&(VS1053模块的标注SI表示MOSI,SO表示MOSI,XCS表示SS)&/p&&p&同时通过查阅STM32F407ZGT6的原理图,我们很容易找到三个SPI对应的引脚接口.在本篇中,我们只使用到了SPI1与SPI2作为外接外设&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-d2fab9f2f67ba200954f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1130& data-rawheight=&739& class=&origin_image zh-lightbox-thumb& width=&1130& data-original=&https://pic3.zhimg.com/50/v2-d2fab9f2f67ba200954f_r.jpg&&&/figure&&p&&br&&/p&&p&因此现在你可以找到W5500和VS1053模块上的标注,并且对照其原理图的标注使用杜邦线把它接到STM32F407ZGT6的核心板上去了.&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-0bd962fce976eb645b7c0fa35948d6ad_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&1067& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/50/v2-0bd962fce976eb645b7c0fa35948d6ad_r.jpg&&&/figure&&p&&br&&/p&&p&&b&SPI是如何工作的&/b&在简短地科普SPI协议以前,我回归到最原始的电这一基本概念上来,当然我们并不打算在这上面夸夸其谈,但说到电不可避免的就是必须谈到电压这一概念,高中数学告诉我们,电压的单位是伏特,我们往往用字母V来表示电压这一单位.&/p&&p&如果两个电压不同的链路相连,电流就会从高电压往低电压“流动”,同时,电压也意味着更多东西。例如人体的安全电压是36V,除非你想“爽一把”,否者我不建议任何人去碰超过安全电压的电线,单片机的供电电压一般为12v,5V或3.3v,同时IO引脚的输出电压一般也不会超过5V,因此你不用担心触碰裸露的引脚会对你的生命安全造成多大的威胁,最后也就是我们主要需要关注的重点,在数电或模电中,我们往往规定某一电压为“低电平”并规定另一个比较高的电压为“高电平”,并以此来当做数学上的0和1(或者相反),因此可以想象,当一条线路上从低电平上升到高电平,实际上是这样一个图形&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-ca51b7de0bc7e5dd2f6140a4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&249& data-rawheight=&156& class=&content_image& width=&249&&&/figure&&p&&br&&/p&&p&如果你仔细看的话,这个上升的过程并不是100%垂直的,但这个上升的时间如此之短,因此在理想的状态下,我们希望这个上升的过程越短越好接近于垂直,我们管这个“上升”的直线叫上升沿。同样的,当这个电压又从高电平到低电平,图形就变为了&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-5fd49a7b7c5f51edc4c72d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&664& data-rawheight=&243& class=&origin_image zh-lightbox-thumb& width=&664& data-original=&https://pic3.zhimg.com/50/v2-5fd49a7b7c5f51edc4c72d_r.jpg&&&/figure&&p&&br&&/p&&p&同样的,我们把这个下降的过程叫下降沿,用时把这个上升又下降的一个过程叫做“脉冲”,因此,一个脉冲肯定包含了一个上升沿和一个下降沿。&/p&&p&实际上“脉冲”这一概念贯穿了整个数字电路的理念,在很多的通讯协议中,一秒钟内完成多少个这种“脉冲”直接和它的通讯速率挂钩,实际上SPI协议四根线中的SCLK引脚,就是不断地循环脉冲这一过程,显然,通讯协议的最终目的就是“读和写”或者叫接收数据发送数据,那么问题来了,什么时候发送数据,什么时候接收数据?&/p&&p&这里就来到了SPI的第一个理念,相位。SPI一共有两种相位,即上升沿采样或降沿采样,现在我们假设SPI被设置为第一边沿采样,那么依照下图&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-6fee08b5c385cb27624b35b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&623& data-rawheight=&327& class=&origin_image zh-lightbox-thumb& width=&623& data-original=&https://pic1.zhimg.com/50/v2-6fee08b5c385cb27624b35b_r.jpg&&&/figure&&p&&br&&/p&&p&MISO为SPI主机输入,MOSI为主机输出,可以看到,在一个个边沿也就是上升沿,SPI从MISO读取数据,我们假设低电平为0,那么SPI读取到的第一个数据就为0,然后在第二个边沿也就是第一个下降沿,MOSI为输出数据,因为MOSI这个时候为高电平。也就是SPI向外发送了一个1,重复这一个过程,SPI就完成了数据通信&/p&&p&在上图的三个脉冲中,SPI读取了数据011,并发送了数据100,因为在一个脉冲中能同时完成一个位的收发,所以这个发送模式又被称为“全双工模式”。假设SCLK的频率是1&/p&&p&024HZ,那么在一秒钟内,通过SPI能够发送与接收的数据分别为字节,如果频率为1MHZ,那么就是256Kbytes/s的速度.&/p&&p&显然的,需要SPI能够完成通讯需要相互商量好何时读写时钟范围&/p&&p&SPI的主要参数如下&/p&&p&CPOL:时钟极性&/p&&p&CPHA:时钟相位&/p&&p&当CPOL为0时,初始状态也就是时钟空闲时电平为低;&/p&&p&当CPOL为1时,时钟空闲时电平为高;&/p&&p&当CPHA为0时,时钟周期的上升沿采集数据,时钟周期的下降沿输出数据;&/p&&p&当CPHA为1时,时钟周期的下降沿采集数据,时钟周期的上升沿输出数据;&/p&&p&&br&&/p&&p&&b&开始编码类型的准备工作&/b&&/p&&p&如果在PC上敲代码,笔者会用下面一张图来表达我的心情&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-05f58cf5ee3a501e4ebbc8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&400& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic1.zhimg.com/50/v2-05f58cf5ee3a501e4ebbc8_r.jpg&&&/figure&&p&但嵌入式裸机开发显然不能那么奔放,你必须小心翼翼地考虑到资源,时钟,运行效率等一系列问题,在某些情况下你必须自行实现内存管理,代码调度等一系列机制,在某些情况下,某些书本上的框架实现将变成瞎扯淡,你必须按照你对资源的理解量身订做适合这个系统的程序.&/p&&p&不管怎么样,笔者在编码的开头都习惯做一些准备工作,例如将关键类型做一下重命名&/p&&p&例如如下代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&#define
_IN[/size]
unsigned int
unsigned short
unsigned short
unsigned int
unsigned char
unsigned long
unsigned long long
unsigned int
unsigned short
unsigned char
&/code&&/pre&&/div&&p&不管怎样,将一些关键字按照自己的规范进行重命名是个好习惯,在接近底层的开发中,我们很多时候需要关注数据的大小与对齐等一系列问题,因此我们不希望编译器与环境的不同导致在不同的地方导致过多的偏差(而导致项目需要大量修改),因此在项目中尽量的使用自定的类型,以便于说在以后的移植中做的代码修改量最少.&/p&&p&另外就是一些常用类型与常亮,例如TRUE和FALSE,NULL这样的经常用得到的常量或者是π这样的数学常量最好也先坐定义,然后_IN _OUT这样的空宏对参数的说明上的标注也非常有好处,这些工作都可以在一个头文件中定义完成.&/p&&p&&br&&/p&&p&&b&时间中断&/b&&/p&&p&在讨论驱动编写之前,我们首先要先处理好定时器,毕竟在驱动编写的过程中,常常不可避免的需要编写延迟函数,如果在PC端开发,也许一个延迟函数仅仅只是需要简单的调用sleep函数就可以了,但在单片机中,你不得不了解时间是怎么来的.&/p&&p&大部分的单片机芯片都有提供时间中断,大致意思是你设置一个寄存器,该寄存器在某一时间间隔都会被减去1或加上1,当这个值变为0或者溢出后,程序就会跳转执行时间中断函数,现在的问题是,如何去设置时间的间隔,在附件中你可以找到stm32f407zgt6芯片的datasheet也就是数据手册,通过查找block diagram你将会看到下面这一张图&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-39d27c195c44ec517c7d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&721& data-rawheight=&878& class=&origin_image zh-lightbox-thumb& width=&721& data-original=&https://pic4.zhimg.com/50/v2-39d27c195c44ec517c7d_r.jpg&&&/figure&&p&上面标注的是个模块连接到的总线是哪块,同时这张图还给出了该总线的时钟频率,如果是新手的话,相信看到这张图已经开始觉得一整反胃了,然而笔者并不会说这张图看起来其实没那么复杂,我想说的是,等你天天被恶心,你总会习惯的,言归正传,假如你实在觉得头晕的话,这张表格下面的一段话给你划出了重点&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-0da43f60d8f32b035a2d7c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1053& data-rawheight=&525& class=&origin_image zh-lightbox-thumb& width=&1053& data-original=&https://pic3.zhimg.com/50/v2-0da43f60d8f32b035a2d7c_r.jpg&&&/figure&&p&&br&&/p&&p&大致意思是当计时器连接到了APB2总线时,他的频率是168MHZ,当连接到APB1总线时,是84MHZ,在默认的情况下,计时器连接到APB2总线,也就是有一个寄存器其每秒钟做168M次减法,我们将这个寄存器,设置为时钟频率的千分之一,我们就能精确地延迟1ms,将它除以百万分之一,就能精确延迟1us&/p&&p&我们通过ST库函数中的SysTick_Config来设置这个计时寄存器,当计时寄存器为0后,它会重新设置为你设置的值,所以这个函数调用一次就够了:&/p&&div class=&highlight&&&pre&&code class=&language-text&&SysTick_Config(00);
&/code&&/pre&&/div&&p&当寄存器到0后,引发时间中断,调用回调函数&/p&&div class=&highlight&&&pre&&code class=&language-text&&void SysTick_Handler(void){}
&/code&&/pre&&/div&&p&这个函数你可以在stm32f4xx_it.c中找到,在笔者的代码中,笔者定义了一个全局变量用于计算从芯片开始工作的时间.我们可以使用TimeGetTime函数来取得这个时间.同时用sleepms来延迟若干毫秒。&/p&&div class=&highlight&&&pre&&code class=&language-text&&//SysTick handler[/size]
unsigned int __g_Timer=0;
void SysTick_Handler(void){g_Timer++;}
uint32_t TimeGetTime(void){return __g_T}
void sleepms(uint32_t ms){
uint32_t CurrentTime=TimeGetTime();
while(TimeGetTime()-CurrentTime&ms);
&/code&&/pre&&/div&&p&&b&驱动架构&/b&&/p&&p&如果按教科书上说的,代码架构应该高内聚低耦合,应该自顶向下,但笔者一直秉承着一句话,脱离环境讲什么架构优秀就是瞎扯淡,纸上谈兵就和说我明天买彩票一定会中奖一样不可靠,换句话说,应该叫只有合适的架构,没有优秀的架构.&/p&&p&不管如何来说,我们设计架构的目的是让代码明了好用,并且保证代码能够不用修改或很少修改就能移植到另外一个平台,毕竟谁都希望在以后少写代码少造同样的轮子.尤其在驱动编写的过程,不可避免的涉及一堆底层操作,如何封装好硬件有关的代码和无关的代码,直接影响到这个代码以后移植是否能做到快速好用.&/p&&p&按照一般的情况而言,应该将总线通讯的初始化代码封装为一块,然后在通讯协议的基础上,编写驱动的工作代码建立硬件虚拟层,最后在最上层调用驱动,实现功能,然而事实是,这种自顶向下的设计模式糟糕的一笔,你不得不花大量的时间封装文档,告诉别人或未来早已经忘记这段代码写的是什么的自己,应该到哪个源代码里去修改参数,再到哪个文件里去修改GPIO引脚,再去哪里改改时间延迟函数,更糟糕的是,假设如果这个设备和其它设备出现共用总线时,你不得不重新再考虑你通讯协议代码封装的在当前环境合不合适了.更有甚者,你需要移植到其它系列芯片中,这不得不让你重写通讯协议代码,你不得不再分出心思来把以前的代码实现全部删掉,然后在注释上写上,本驱动不实现通讯代码,你需要打开xx文件,在哪里添加初始化代码,在哪里添加发送数据代码,在哪接收……相信我,这所谓的自顶向下简直蠢爆了.&/p&&p&其实笔者总结了下驱动开发的规律,基本上九成以上的驱动,总结起来无非就是数据读和写的加上IO引脚控制和时间延迟的问题,至于通讯协议,无非还不是为了完成读写操作,本身并没有什么特别的内容,为什么不使用一个函数指针来规定驱动的读写与IO操作函数,这样我们就能够将驱动和通讯协议IO口操作剥离开来.实际上笔者使用这一方案多年,并且觉得确实在大部分的环境中工作良好,效率高效&/p&&p&实现的代码实际非常简单,定义一个PX_Linker结构体&/p&&div class=&highlight&&&pre&&code class=&language-text&&typedef struct __PX_LINKER[/size]
px_bool (* _PX_LinkerInit)(px_void *Info);
(* _PX_LinkerWrite)(_IN px_void *buffer,px_int size);
(* _PX_LinkerRead) (_OUT px_void *buffer,px_int size);
px_int (* _PX_LinkerIOCTL)(_IN px_int ioctl,_IN px_int io,_IN px_void *param);
&/code&&/pre&&/div&&p&当中包含四个函数指针,一个data类型指针&/p&&p&其中_PX_LinkerInit用于实现通讯协议的初始化代码, _PX_LinkerWrite用于实现写数据代码_PX_LinkerRead用于实现读数据代码_PX_LinkerIOCTL则用于实现所有其他的控制代码,包括延迟和GPIO口的控制.&/p&&p&同时我们提供四个函数对PX_Linker进行操作避免直接操作结构体&/p&&p&其中PX_LinkerInit用于对PX_Linker进行初始化,包括设置其四个函数指针,函数原型如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&px_bool
PX_LinkerInit(PX_Linker *linker, px_void *Init,px_void *Write,px_void *Read,px_void *ioctl,px_void *param);
&/code&&/pre&&/div&&p&PX_LinkerWrite为一个宏定义,其目的仅仅只是调用写函数,其宏定义规则如下.其中lnk为指向PX_Linker的指针,buffer为写buffer,size为希望写入的字节数。&/p&&div class=&highlight&&&pre&&code class=&language-text&&#define PX_LinkerWrite(lnk,buffer,size) ((lnk)-&_PX_LinkerWrite(buffer,size))
&/code&&/pre&&/div&&p&PX_LinkerRead为一个宏定义,其目的仅仅只是调用读函数,其宏定义规则如下.其中lnk为指向PX_Linker的指针,buffer为读取buffer指针,size为缓存区最大的大小.&/p&&div class=&highlight&&&pre&&code class=&language-text&&#define
PX_LinkerRead(lnk,buffer,size) ((lnk)-&_PX_LinkerRead(buffer,size))
&/code&&/pre&&/div&&p&PX_LinkerIOCTL用于调用PX_Linker中的控制函数.其中参数IOCTL为控制标识符.io及param都为传入参数.&/p&&div class=&highlight&&&pre&&code class=&language-text&&#define
PX_LinkerIOCTL(lnk,ioctl,io,param) ((lnk)-&_PX_LinkerIOCTL(ioctl,io,param))
&/code&&/pre&&/div&&p&在编写驱动时,我们只需要在上层(例如main函数中)去考虑芯片相关的通讯协议,同时实现几个PX_Linker 所需要的代码实现,然后将PX_Linker传递给驱动程序的逻辑实现当中就可以了,因为不包含主控芯片相关的代码,因此驱动的代码直接拷贝到其它的项目中也不会出什么问题,需要做的就仅仅只是重新在自己喜欢的地方完成通讯总线的实现就可以了.&/p&&p&&br&&/p&&p&&b&VS1053b驱动程序开发SPI协议&/b&&/p&&p&VS1053连接到了STM32F407ZGT6的SPI2接口上,你可以在附件中找到VS1053B的datasheet,有关SPI通讯协议的要求你可以在DataSheet的第18页找到&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-622dc695c10e188af69ddb19_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&605& data-rawheight=&262& class=&origin_image zh-lightbox-thumb& width=&605& data-original=&https://pic1.zhimg.com/50/v2-622dc695c10e188af69ddb19_r.jpg&&&/figure&&p&&br&&/p&&p&可以看到,第一个数据在SCK的第一个上升沿发生时,为了保证建立保持时间(就是电瓶变换后电压的维持时间),这个上升沿应该在半个周期后&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-b93b9ad1d12d62d10c633e4bd80fb86d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&495& data-rawheight=&275& class=&origin_image zh-lightbox-thumb& width=&495& data-original=&https://pic3.zhimg.com/50/v2-b93b9ad1d12d62d10c633e4bd80fb86d_r.jpg&&&/figure&&p&&br&&/p&&p&因此,SPI的时钟初始电平应该设置为高,在第一个上升沿就是脉冲的第二个边沿.除此之外,在Datasheet的第23页还提到了&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-091ff72e30bd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&704& data-rawheight=&209& class=&origin_image zh-lightbox-thumb& width=&704& data-original=&https://pic2.zhimg.com/50/v2-091ff72e30bd_r.jpg&&&/figure&&p&SPI的最大速度不得超过CLKI的六分之一,第三段提到,在初始状态下,CLKI=XTALI,通过对datasheet的进一步查找&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-14bc9b438cedd87c6b49_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&571& data-rawheight=&210& class=&origin_image zh-lightbox-thumb& width=&571& data-original=&https://pic3.zhimg.com/50/v2-14bc9b438cedd87c6b49_r.jpg&&&/figure&&p&XTALI的速度应该在12-13MHZ之间,因此,SPI的速度不应该超过大约2.16MHZ,我们回到STM32F407ZGT6的数据手册上来,从图中我们可以看出,SPI2属于APB1总线(由AHB二分频而来),其最大的速率为42MHZ&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-e451c099aa7ccbbf7ea0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&459& data-rawheight=&401& class=&origin_image zh-lightbox-thumb& width=&459& data-original=&https://pic2.zhimg.com/50/v2-e451c099aa7ccbbf7ea0_r.jpg&&&/figure&&p&&br&&/p&&p&因此为了通讯速度不大于2.16MHZ,它至少应该被32分频.&/p&&p&同时我们查看SPI的时序图&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-f1ff137bc02f2d5d05ee24e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&583& data-rawheight=&229& class=&origin_image zh-lightbox-thumb& width=&583& data-original=&https://pic3.zhimg.com/50/v2-f1ff137bc02f2d5d05ee24e_r.jpg&&&/figure&&p&得知,其数据为8位,同时高位在前低位在后.&/p&&p&因此,SPI2的初始化代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); [/size]
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullD //全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_M
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位
SPI_InitStructure.SPI_CPOL
= SPI_CPOL_H//极性
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2E//相位
SPI_InitStructure.SPI_NSS = SPI_NSS_S
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;//波特率32分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位在前地位在后
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2,&SPI_InitStructure);
SPI_Cmd(SPI2,ENABLE);
&/code&&/pre&&/div&&p&&b&VS1053b命令与寄存器&/b&&/p&&p&不管怎么说,对硬件驱动的编写第一步个人还是比较喜欢先完成硬复位这一功能&/p&&p&在Datasheet的9.2章节中,复位可以通过对XRESET也就是RST引脚拉低来完成,在拉低RST引脚后,DREQ引脚会拉低22000个时钟,也就是说,假如VS1053b运行在12.288MHz的频率,复位会有个1.8ms左右的延迟,当然,你可以通过判断DREQ引脚是否回到高电平来判断芯片是否成功复位.&/p&&p&之后是编写VS1053的最基本操作功能了,对VS1053b的操作是通过寄存器的读写来完成的,在VS1053b的datasheet中,管这种操作叫SCI(Serial Command Interface)下面两张时序图,分别是对VS1053b的寄存器读与写的时序图:&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-9a53a9d14eea340a5ac2a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&630& data-rawheight=&298& class=&origin_image zh-lightbox-thumb& width=&630& data-original=&https://pic1.zhimg.com/50/v2-9a53a9d14eea340a5ac2a_r.jpg&&&/figure&&p&&br&&/p&&p&同时,在下图中给出了SCI读写的命令格式:&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-15fd91b38e06e16dcb8d0ef8c73c1c27_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&254& data-rawheight=&90& class=&content_image& width=&254&&&/figure&&p&从时序图与给出的格式我们可以知道,在命令读的时候,首先先将XCS引脚拉低以片选设备,然后需要发送一个0x03表示读命令的操作码,最后发送一个16位地址,在此之后,该地址的值将会由MISO传输回来.&/p&&p&在命令写的时候,拉低XCS片选,发送一个0x02表示写命令的操作码,之后发送一个16位地址,之后发送一个16位的值,最后需要拉高XCS引脚,在命令执行期间,QREQ引脚将会被拉低至低电平,直到命令完成,才会回到高电平状态.&/p&&p&在到这一步的时候,我们基本可以完成VS1053b驱动最核心的部分了.&/p&&p&首先,我们知道,VS1053b除了SPI所需的三个引脚外(SS引脚由软件控制),还有XCS,RST,DREQ,XDCS三个引脚,因此,在控制函数中,除了对延迟函数的实现,还有对三个引脚的控制&/p&&p&下面的代码实现了SPI的读写和IOCTL(IO controller),我们完成这个代码,并将它设置给PX_Linker中(我们将XCS连接到PB12,DREQ连接到PB10.XDCS连接到PB11,XRST连接到PB9)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&font style=&font-size:10.5pt&&&font style=&font-size:10.5pt&&px_int PX_PROTOCAL_VS10xx_IOCTL(px_int ioctl,px_int io,px_void *param)
px_u16 PIN;
if(ioctl==PX_DEVICE_VS10xx_IOCTL_XDCS) PIN=GPIO_Pin_11;
if(ioctl==PX_DEVICE_VS10xx_IOCTL_XCS) PIN=GPIO_Pin_12;
if(ioctl==PX_DEVICE_VS10xx_IOCTL_DREQ) PIN=GPIO_Pin_10;
if(ioctl==PX_DEVICE_VS10xx_IOCTL_RST) PIN=GPIO_Pin_9;
switch(ioctl)
case PX_DEVICE_VS10xx_IOCTL_SLEEPMS:
sleepms(v);
case PX_DEVICE_VS10xx_IOCTL_XDCS:
case PX_DEVICE_VS10xx_IOCTL_XCS:
case PX_DEVICE_VS10xx_IOCTL_RST:
GPIO_SetBits(GPIOB, PIN);
GPIO_ResetBits(GPIOB, PIN);
case PX_DEVICE_VS10xx_IOCTL_DREQ:
return GPIOB-&IDR & PIN;
case PX_DEVICE_VS10xx_IOCTL_SPI_HIGHSPEED:
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler_4));
SPI2-&CR1&=0XFFC7;
SPI2-&CR1|=SPI_BaudRatePrescaler_4;
SPI_Cmd(SPI2,ENABLE);
case PX_DEVICE_VS10xx_IOCTL_SPI_LOWSPEED:
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler_256));
SPI2-&CR1&=0XFFC7;
SPI2-&CR1|=SPI_BaudRatePrescaler_32;
SPI_Cmd(SPI2,ENABLE);
px_int PX_PROTOCAL_VS10xx_Write(px_void *buffer,px_int size)
px_char *p=(px_char *)
while(size--)
while((SPI2-&SR&SPI_I2S_FLAG_TXE)==RESET);
SPI2-&DR = *p;
while((SPI2-&SR&SPI_I2S_FLAG_RXNE)==RESET);
px_int PX_PROTOCAL}

我要回帖

更多关于 飞利浦50puf6192/t36 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信