沈婷婷

个性签名:坚持自己的坚持,终究会有拨开云雾的一天

  • 2019-06-05
  • 回复了主题帖: 请问有没有人使用过AMIS-30543驱动过步进电机啊

    chen_yukie 发表于 2019-5-7 13:11 pwm频率固定的话要怎么调速啊?
    PWM波频率改变才能调速

  • 2019-05-22
  • 回复了主题帖: 最近在使用powerSTEP01驱动86的步进电机,大家谁有这个芯片的驱动啊?

    Amore 发表于 2019-5-22 14:22 厉害了
    哈哈,没有了,花了很长时间

  • 回复了主题帖: 你身边有“大理论家”吗?

    可能就是不会,不要总是要求别人,有些人也会招人烦,但总自己决定不了别人

  • 2019-05-21
  • 回复了主题帖: 【RT-Thread读书笔记】第一部分 内核简单原理

    厉害

  • 2019-05-13
  • 回复了主题帖: 【RT-Thread读书笔记】7-10章读书笔记

    zyfeng2019 发表于 2019-5-11 22:09 值得好好学习
    是啊!书中讲的挺好!

  • 2019-05-11
  • 发表了主题帖: 【RT-Thread读书笔记】7-10章读书笔记

       前面第6章实现的线程的定义与线程切换的实现没有支持多优先级,只支持两个线程的互相切换,下面给大家介绍实现线程优先级的功能,在RT-Thread中,数字优先级越小,逻辑优先级越高。RT-Thread是一个根据优先级来调度的抢占式实时操作系统,即每个系统周期到来时,调度器都会扫面就绪列表,选取优先级最高的线程去执行。   在介绍实现优先级之前,先给大家介绍几个概念,第一个是临界段的保护,临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在RT-Thread里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪靶子,谁都可以对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁中了靶子,为了保护临界段,我们要实现关中断函数,开中断函数。如临界段代码的应用在进入临界段之前,我们会把中断关闭,退出临界段时再把中断打开。第二个是对象容器的实现。在RT-Thread中,所有的数据结构都称为对象。如:线程,信号量、互斥量、事件、邮箱、消息队列、内存堆、内存池、设备和定时器等。在RT-Thread中,为了方便管理这些对象,专门定义了一个对象类型数据结构,代表了数据类型。在RT-Thread中,每个对象都会有对应的一个结构体,这个结构体叫做该对象的控制块。如线程会有一个线程控制块。在控制块的开头放置的是对象结构体的成员,具体的见书里面。什么是容器,在RT-Thread中,每当用户创建一个对象,如线程,就会将这个对象放在一个叫做容器的地方,这样做的目的是为了方便管理,通过扫描容器的内核对象来获取各个内核对象的状态,然后输出调试信息。容器的接口实现方式:容器在定义的时候,大小被固定了,但容器里面的成员是否初始化就不一定了,从容器里获取指定类型的对象函数会遍历整个容器对象,如果对象的类型等于我们指定的类型,那么就返回该容器成员的地址。每创建一个对象,都需要先将其初始化,主要分成两个部分的工作,首先将对象控制块里面与对象相关的成员初始化,然后将该对象插入到对象容器中,对象初始化函数在线程初始化函数里面被调用。第三个比较重要的概念是空闲线程与阻塞延时的实现。当线程需要延时,进入阻塞状态,需要cpu执行空闲线程。   如在下一个系统周期来临时,调度器需要选取优先级最高的线程去执行,在程序里我们用一个32位的数表示线程的优先级。我们规定在这个32位的数中,位数越低,优先级越高,如第一个置1的位是位1,即表示此时就绪的线程当中,优先级最高的是线程1,然后调度器从线程优先级表的索引1下取出线程的线程控制块,从而切换到线程1。但是,单片机没有眼睛,并不能跟人一样一眼就从线程就绪优先级组中看到那个第一个置1的位,怎么办?专门写了一个函数,通过查表的方法获得最高优先级的线程。我们通过线程优先级组获得最高的优先级线程。然后根据线程优先级表即就绪列表,我们可以理解为就绪列表就是线程优先级表。线程优先级表是全局数组。线程优先级的数据类型是rt_list,每个索引号对应线程的优先级,该索引下维护着一条双向链表,当线程就绪时,线程就会根据优先级插入到对应索引的链表,同一个优先级的线程都会被插入到同一条链表中。最后我们将线程插入到线程优先级表和移除分别用rt_schedule_insert_thread()和rt_schedule_remove_thread()这两个函数实现。通过就绪优先级组和线程优先级表就可以实现多优先级的线程了。然后根据优先级来决定第一个运行的线程。而系统调度函数也通过线程的优先级进行切换。根据优先级切换的原理如下:首先获取就绪的最高优先级,然后获取就绪的最高优先级对应的线程控制块,如果目标线程不是当前线程就要进行线程切换。修改阻塞延时函数rt_thread_delay()的实现原理:将线程的状态改为挂起,接下来将进入延时,暂时放弃CPU的使用权。然后根据优先级将线程就绪优先级组中对应的位清零。还有一些函数才能最终实现线程的优先级,今天实在太饿了,没有精力完成了。 此内容由EEWORLD论坛网友沈婷婷原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2019-05-08
  • 回复了主题帖: GD32E231 DIY大赛(5)——完成DS1302的驱动和设置及长短按键识别

    {:1_103:}

  • 2019-05-06
  • 回复了主题帖: 【RT-Thread读书笔记】4. RT-Thread 学习6章读后感(一)

    我想问问你,创建线程是在单独的一个.c文件中吗?

  • 回复了主题帖: 【RT-Thread读书笔记】——线程的定义与线程切换的实现

    数码小叶 发表于 2019-5-6 12:59 个人意见,宁可读懂一章,也不速读十章。没意义
    这就一章!书中几页纸的内容!我学习很慢,不是特别聪明

  • 回复了主题帖: 【RT-Thread读书笔记】5. RT-Thread 学习6章读后感(二)

    传媒学子 发表于 2019-5-5 18:22 都是书上的知识,自己稍微总结提炼了一点,也在学习中。。
    有学习的心态和精神就好。

  • 发表了主题帖: 【RT-Thread读书笔记】——线程的定义与线程切换的实现

       4月30日看到大家都纷纷发了自己的读书感受我有些着急了,匆匆忙忙的发了初始读《RT-Thread内核实现与应用开发实战指南》的初始感受,当我发了一篇后,我就静下心来读别人的读书笔记了,我发现了自己和他人的几点差距,我写的东西,没有标注出重点,让读者不易读,很多东西前后的逻辑不够,而感受颇多。我做过的工程有CAN通讯,LWIP的移植,SPWM驱动步进电机,AMIS30543驱动42步进电机,powerstep01驱动86步进电机。所有的这些工程,从编译的hex文件来看,要数LWIP的工程最大,仍使用前后台可以实现。我至今不知道什么时候必须用操作系统的方式进行编程,不过我通过学习RT-Thread内核实现与应用开发实战指南认识到嵌入式操作系统的一大优势是实时性,但是相对于裸机系统的规范,要求,概念就多太多了。因为我不理解多线程系统,我又翻看了书中多线程的概念:相比前后台系统,多线程系统的事件响应也是在中断中完成的,但是事件的处理是在线程中完成的。在多线程系统中,线程跟中断一样,也具有优先级,优先级高的线程会被优先执行。当一个紧急的事件在中断被标记之后,如果事件对应的线程的优先级足够高,就会立马得到响应。相比于前后台系统,多线程的实时性又被提高了。在多线程系统中,根据程序的功能,我们把这个程序主体分割成一个个独立的,无限循环且不能返回的小程序,这个小程序我们称之为线程。每个线程都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。对于书中的一句话:“加入操作系统,我们的编程反而变得简单了。整个系统随之带来的额外开销就是操作系统占据的那一丁点的FLASH和RAM。”我不理解,对于我来说嵌入式操作系统的很多定义,概念运行原理,我一头雾水,这句话既没有给我增加信心,还让我对自己提出了质疑。   在书中的第6章——线程的定义与线程切换的实现,这一章的重要性让我难以用词汇描述,可以说当我们建造高楼大厦的时候,这章的内容就像高楼大厦的地基,地基打不好,高楼大厦容易倒塌。如果用我们从小到大的学习生涯来比喻,这一章的内容就像我们学习的拼音语言一样重要。那么大家肯定疑问:“什么是线程呢?”在书中这样描述:在多线程系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为线程。这句话描述了线程的特点,但是我却疑问线程到底有怎么样的优势。带着这样的疑问我只能继续去了解线程的实现。在定义线程栈这一节的内容讲了:“在线程和裸机系统的区别是:在裸机系统中,如果有全局变量,有子函数调用,有中断发生。那么系统在运行的时候,全局变量放在哪里,子函数调用时,局部变量放在哪里,中断发生时,函数返回地址放在哪里,我们不用管。而写一个RTOS,这些种种环境参数,我们必须弄清楚他们是如何存储的。在裸机系统中,他们统统放在一个叫栈的地方,栈是单片机RAM中里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里指定,最后由C库函数main进行初始化。但是,在多线程系统中,每个线程都是独立的,互不干扰的,所以要为每个线程分配独立的空间,这个栈空间通常是一个预先定义好的数组,也可以是动态分配的一段空间,但它们都存在RAM中。”这里再次强调了线程栈的优点:独立性。这一点,我在理解的时候一定要牢记。线程是一种特殊的函数,这个函数的特点是独立的,函数主体无线循环且不能返回。而在主函数里面实现线程的转换就需要定义线程的控制块。在定义线程控制块这一节,书中讲到:“在裸机系统中,程序的主体是CPU按照顺序执行的。而在多线程系统中,线程的执行是系统调度的。系统为了顺利的调度线程,为每个线程都额外定义了一个线程控制块,这个线程控制块就相当于线程的身份证,里面存有线程的所有信息,比如线程的栈指针,线程的名称,线程的形参等。有了这个线程控制块就相当于线程之后,以后系统对线程的全部操作都可以通过这个线程控制块来实现。定义一个线程控制块需要一个新的数据类型,该数据类型在rtdef.h这个头文件中声明,使用它可以为每个线程定义一个线程控制块实体。”除了定义线程控制块,线程的栈,线程的实体函数最终需要联系起来才能由系统进行统一调度。这个联系的过程我们叫做线程的初始化函数,在这个函数的函数体中一个非常重要的概念我并不懂得应用,这个概念是链表,书中这样讲到:“在初始化线程链表节点,往后我们把线程插入到各种链表中,就是通过这个节点来实现的,它就好像是线程控制块里面的一个钩子,可以把线程控制块挂在各种链表中,在初始化之前我们需要在线程控制块中添加一个线程链表节点。”这里我对链表的理解还是一知半解。希望有人能帮我解释一下!   在这里补充一下百度对链表的解释:“链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。”对于单个链表的初始化就是将节点里面的next和prev这两个节点指针指向节点本身。在双向链表表头后面插入一个节点第一步是将以前前面prev指针指向新节点。第二步将新节点的next指针指向1节点的next指针指向的节点,第三步将1的next指针指向n节点,n的prev指针指向1节点。像双向链表的表头后面插入一个节点,从双向链表删除一个节点和以上说的操作类似。初始化线程函数的第二步就是将线程入口保存到线程控制块的entry成员中。以及其他的成员都进行相应的保存。最后一步是初始化线程栈,并返回线程栈顶指针。当线程第一次运行的时候,加载到CPU寄存器的环境参数我们要预先初始化好。从栈顶开始,初始化的顺序固定,首先是异常发生时自动保存的8个寄存器,即xPSR、R15、R14、R12、R3、R2、R1和R0。其中xPSR寄存器的位24必须是1,R15的PC指针必须存的是线程的入口地址,R0必须是线程形参,剩下的R14、R12、R3、R2和R1我们初始化为0。剩下的是8个需要手动加载到CPU寄存器的参数,即R4~R11,默认初始化为0xdeadbeaf。初始化线程以后,书中继续讲解了如何实现就绪列表。线程创建好以后,我们需要把线程添加到就绪列表里面,表示线程已经就绪,系统随时可以调度,进一步书中讲解了如何实现调度器。  调度器是操作系统的核心,其主要功能就是实现线程的切换,即从就绪列表里面找到优先级最高的线程,然后去执行该线程。调度器在使用之前必须先初始化。首先定义一个局部变量,用C语言关键词register修饰,防止被编译器优化。初始化线程就绪列表,初始化完后,整个就绪列表为空。然后初始化当前线程控制块指针为空。rt_current_thread是定义的一个全局指针,用于指向当前正在运行的线程的线程控制块。一般我们把调度器初始化放在硬件初始化之后,线程创建之前。然后我们再启动调度器。  如何启动调度器呢?启动调度器函数名是void rt_system_scheduler_start()函数,调度器在启动的时候会从就绪列表中取出优先级最高的线程的线程控制块,然后切换到改线程。但是目前我们的线程还不支持优先级,那么就手动指定第一个运行的线程为就绪列表下标为0的这条链表里面挂着的线程。rt_list_entry()是一个已知一个结构体里面的成员的地址,反推出结构体的首地址的宏。rt_hw_context_switch_to()函数实现第一次切换到新的线程。这是一个汇编语言实现的函数。在此函数中PendSV_Handles()函数是真正实现线程上下文切换的地方。    我们知道了调度器的实现,下一步就是应用调度器实现系统调度。系统调度就是在就绪表中寻找优先级最高的就绪线程,然后去执行改线程。但是目前我们还不支持优先级,仅实现两个线程轮流切换,系统调度函数rt_schedule()实现。rt_hw_contex_switch()函数用于产生上下文切换。    最后,就是整个的main()函数了,main函数首先对硬件初始化,然后初始化线程1,接着将线程1插入到就绪表中,然后初始化线程2,将线程2插入到就绪表中,启动系统调度器,在线程中实现线程的切换。       载或用于商业用途需征得作者同意并注明出处

  • 回复了主题帖: 基于STM32103x的BLDC(PMSM)控制器原理图

    厉害了!

  • 2019-05-05
  • 回复了主题帖: 【RT-Thread读书笔记】5. RT-Thread 学习6章读后感(二)

    厉害

  • 2019-04-30
  • 回复了主题帖: 不可一世的数学遇到高手之后也会放低她高傲的身段

    这个文章很多博客上有,我看过很多遍,真得特别好!

  • 发表了日志: 【RT-Thread读书笔记】——越努力越幸运

  • 发表了主题帖: 【RT-Thread读书笔记】——越努力越幸运

    4月13日期待已久的RT-Thread内核实现与应用开发实战指南基于STM32的书籍到了。之所以期待,是因为这本书是我参加电子工程世界的测评活动得到的书,展示一下收到的书籍。我和野火的缘分还不止这些,学习STM32一年半的时间,野火给了我很大的帮助,展示一下我参考的书籍。   我非常喜欢读野火的《STM32库开发实战指南》的第一章:为什么学习STM32,在第一章的第二节写了嵌入式工程师成长之路。一个人拥有更多的技能,能够做更多的事,一直鼓励着我不断学习。同样在《RT-Thread内核实现与应用开发实战指南》的第二章:为什么学习RTOS里面一句话与大家共勉:作为一个合格的嵌入式软件工程师,学习是永远不能停歇的事,时刻都得为将来准备。书到用时方恨少,我希望机会来临时你不要有这种感觉。接触STM32的时间是在2017年底,可是对于电子行业我在大学大二暑假的时候,参加电子设计竟赛,我的自学能力比较差,对于单片机的应用理解的也少,那次经历只让我有了深深的挫败感。一晃6年过去了,工作一开始没有做技术,直到工作不顺心,压抑。两年前让我坚持选择了做技术。   目前我虽然没有做过实时操作系统的工程,但是以我做过的工程的经验,嵌入式的应用更加广泛,长话短说,我想说一下,我通过《RT-Thread内核实现与应用开发实战指南》这本书,学到了那些RT——Thread的编程思想。在书的第5章讲了裸机系统和多线程系统的区别。裸机系统又分为轮询系统和前后台系统。轮询系统即是在裸机编程的时候,先初始化相关的硬件,然后让主程序在一个死循环里面不断循环,顺序地做各种事情。轮询系统是一种非常简单的软件结构,通常只适用于那些只需要顺序执行代码且不需要外部事件来驱动就能完成的事情。轮询系统只适合顺序执行的功能代码,当有外部事件驱动时,实时性就会降低。前后台系统相比轮询系统,前后台系统是在轮询的基础上加入了中断。外部事件的响应在中断里面完成,事件的处理还是回到轮询系统中完成,中断在这里我们称为前台,main函数里面的无线循环我们称为后台。多线程系统:相比前后台系统,多线程系统的事件响应也是在中断中完成的,但是事件的处理是在线程中完成的。在多线程系统中,线程跟中断一样,也具有优先级,优先级高的线程会被优先执行。当一个紧急的事件在中断被标记之后,如果事件对应的线程的优先级足够高,就会立马得到响应。相比于前后台系统,多线程的实时性又被提高了。在多线程系统中,根据程序的功能,我们把这个程序主体分割成一个个独立的,无限循环且不能返回的小程序,这个小程序我们称之为线程。每个线程都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。加入操作系统,我们的编程反而变得简单了。整个系统随之带来的额外开销就是操作系统占据的那一丁点的FLASH和RAM。第6章介绍了线程的定义与线程切换的实现,什么是线程?在裸机系统中,系统的主体就是main函数里面顺序执行的无限循环,这个无限循环里面CPU按照顺序完成各种事情。在多线程系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为线程。在线程和裸机系统的区别是:在裸机系统中,如果有全局变量,有子函数调用,有中断发生。那么系统在运行的时候,全局变量放在哪里,子函数调用时,局部变量放在哪里,中断发生时,函数返回地址放在哪里,我们不用管。而写一个RTOS,这些种种环境参数,我们必须弄清楚他们是如何存储的。在裸机系统中,他们统统放在一个叫栈的地方,栈是单片机RAM中里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里指定,最后由C库函数main进行初始化。但是,在多线程系统中,每个线程都是独立的,互不干扰的,所以要为每个线程分配独立的空间,这个栈空间通常是一个预先定义好的数组,也可以是动态分配的一段空间,但它们都存在RAM中。在多线程系统中,有多少个线程就需要定义多少个线程栈。线程函数的特点:线程是一个独立的函数,函数主体无限循环且不能返回。线程控制块的定义:在裸机系统中,程序的主体是CPU按照顺序执行的。而在多线程系统中,线程的执行是系统调度的。系统为了顺利的调度线程,为每个线程都额外定义了一个线程控制块,这个线程控制块就相当于线程的身份证,里面存有线程的所有信息,比如线程的栈指针,线程的名称,线程的形参等。有了这个线程控制块就相当于线程之后,以后系统对线程的全部操作都可以通过这个线程控制块来实现。定义一个线程控制块需要一个新的数据类型,该数据类型在rtdef.h这个头文件中声明,使用它可以为每个线程定义一个线程控制块实体。实现线程创建函数:线程的栈,线程的函数实体,线程的控制块最终需要联系起来才能由系统进行统一调度。我们可以写一个初始化函数来实现,这个初始化函数包含初始化线程控制块指针。初始化线程链表节点,线程的入口函数,线程参数,用于指向线程栈的起始地址,以及线程栈的大小。在初始化线程链表节点,往后我们把线程插入到各种链表中,就是通过这个节点来实现的,它就好像是线程控制块里面的一个钩子,可以把线程控制块挂在各种链表中,在初始化之前我们需要在线程控制块中添加一个线程链表节点。链表节点数据类型的节点里面有两个相同类型的节点指针next和prev,分别用来指向链表中的下一个节点和上一个节点。还有如何实现初始化链表节点,在双向链表表头后面插入一个节点,在双向链表表头前面插入一个节点,以及从双向链表删除一个节点。截止到今天,我只懂得这些,我会利用接下来的时间继续读这本书,来和大家分享。我读书的过程比较慢,方法也不太好,望见谅! 此内容由EEWORLD论坛网友沈婷婷原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 回复了主题帖: 最近在使用powerSTEP01驱动86的步进电机,大家谁有这个芯片的驱动啊?

    沈婷婷 发表于 2019-4-1 17:45 谢谢
    终于写完了程序!

  • 回复了主题帖: 有谁测试通过用STM32驱动nRF24L01,我的程序测试不通过

    找到了

  • 2019-04-10
  • 回复了主题帖: 【书籍已全部寄出】【读书月】读RT-THREAD技术好书活动入选名单公布

    okhxyyo 发表于 2019-4-9 14:49
    发了电脑路径做什么?

  • 回复了主题帖: 有谁测试通过用STM32驱动nRF24L01,我的程序测试不通过

    huo_hu 发表于 2019-4-9 15:08 24L01弄过完整的,真spi收发切换和中断. 你这个是例子程序,下到板子上应该能运行,具体卡在哪里得具体分析一 ...
    寄存器写法,我找到问题了,调通了

统计信息

已有88人来访过

  • 芯币:165
  • 好友:10
  • 主题:12
  • 回复:56
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


huo_hu 2018-8-9
沈婷婷: 你好,我做步进电机驱动,电机转速达到150转每分钟的时候就震动的严重,请问怎么解决啊?
怎么个振动,丢步了吗?力矩够吗?
查看全部