2013-12-07 00:35:58 nicekwell 阅读数 3608
  • 51单片机综合项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

本文是2013年写的,后来整理成了系统文章,请访问 http://nicekwell.net/ 查看单片机编程系列文章。

以下是2013年原文:


以前对单片机编程做过一些零散的总结,近两个月又学习了一下操作系统,对操作系统原理和实现有了初步了解。所以就想到写一篇长文总结,系统总结一下单片机编程和操作系统原理,作为一个从单片机编程到操作系统的过渡文章。

本文介绍了各种单片机编程结构,可以算是本人单片机编程的一点小小的经验。从这些单片机编程结构的不断变化中逐渐过渡到操作系统,并在s3c6410上一步一步实现一个小操作系统。

一开始想要作为一个帖子分享的,但是内容太多,所以做成了一个PDF文件,看上去比较正规,调理也清晰一点,但是内容也就停留在帖子的水平。希望能给从单片机往操作系统过渡的童鞋带来一点帮助,正如我学操作系统时也看了别人的文章一样。

文章的PDF文件可以从http://download.csdn.net/detail/nicekwell/6671759或者http://pan.baidu.com/s/1elUFv下载到。

以下是文章的前言和目录。

 

 

前言

在2011年年初,笔者开始了单片机的学习,那时还是学生的我已经深深地被单片机吸引。之后几乎放弃了学校课程,把所有精力都放在单片机上,参加了一些比赛,并取得不错的成绩。

在开始工作之后,笔者从事单片机开发。在工作期间的高强度编程下,尝试了多种单片机编程结构,对单片机各种结构的构建做了分析和总结,并深刻感受到操作系统产生的必要性,对操作系统的探索有着越来越强的欲望。

在工作一段时间之后,笔者决定辞职。我不想凭仅有的那么一点单片机知识做一个嵌入式码农,我渴望学习新的知识,掌握更高级的理论……好吧~至少做一个高级一点的码农。而下一步的目标就是——linux。

但是笔者并没有直接进行linux的学习,因为操作系统对我来说还是个新事物,我甚至不知道操作系统和单片机编程的最本质区别是什么,于是我想先搞清楚操作系统的本质原理之后再进行linux的学习。笔者是物理学专业的,并没有系统学习过操作系统的概念。事实上很多介绍操作系统的书籍也没有提到操作系统的底层实现,更没有介绍它与单片机编程之间的关系。正如我在辞职的这段时间学完uCOS之后,对操作系统内核构建有了了解,但是却仍然不知底层的任务切换是如何实现的。

However,经过两个月的学习,笔者终于清楚了操作系统的基本原理,也深刻体会到操作系统和单片机各种编程结构的区别和联系。于是我想到把之前的单片机编程经验和近期对操作系统的学习结合到一起,写一个从单片机到操作系统过渡的文章。一方面对单片机编程结构做一个系统总结,另一方面对操作系统原理也进行一次整理。把操作系统和单片机编程整合到一个知识体系中去,以便日后接受更多的操作系统内核知识。

 

本文分为两部分——“单片机编程篇”和“操作系统篇”。

单片机编程篇主要介绍单片机的各种编程结构,及其实现方法。是在假设已经可以驱动单片机和各个模块的前提下,讨论如何整合和使用这些资源,以实现功能。在这一篇中,将会看到程序主体逐渐从主函数转移到定时器,并且明确这些变化的目的。最后还会接触到面向对象的程序设计方式,并体会这种方式带来的好处。

操作系统篇将会介绍操作系统的最基本的任务切换原理,以及操作系统是如何实现在单片机编程中难以实现的功能的。并在arm平台上实现任务切换和简单的操作系统。至于更复杂的操作系统内核构建本文没有多说,各种常用的操作系统内核都有很多资料可以查阅,不过我会向大家推荐一本在网上公开但没有出版的书——《底层工作者手册之嵌入式操作系统内核》,这本书详细阐述了操作系统内核的构建方法。

 

 

目录

第一篇  单片机编程

第1章 主函数顺序调用     2

1.1 主函数顺序调用的一般结构... 2

1.2 主函数顺序调用结构的特点... 2

第2章 界面函数结构     4

2.1 界面函数一般结构... 4

2.2 更高的角度分析这种结构... 5

第3章 定时器分配任务     8

3.1 用界面函数构成的基础框架... 8

3.2 结合定时器编程分析... 9

3.3 任务分割... 11

3.4 定时器分配任务程序结构总结... 12

第4章 占用式与非占用式程序结构分析     14

4.1 什么是占用式程序... 14

4.2 占用式程序的缺点... 14

4.3 对占用式程序的改造... 15

4.4 改造的本质... 16

4.5 非占用式程序结构的优势... 17

4.6 非占用式程序的一般结构... 17

4.7 吐槽... 18

第5章 定时器执行任务     19

5.1 定时器执行任务的程序结构... 19

5.2 定时器里面任务函数的特点... 20

5.3 过程任务的定时器化... 21

5.4 定时器执行任务程序结构总结... 25

5.5 我们追求的是什么... 25

第6章 面向对象思想+事件驱动结构     27

6.1 对象和事件... 27

6.2 C语言对一个对象的封装... 28

6.3 事件分配机制... 31

6.4 系统层构建... 33

6.5 库函数... 33

第二篇  操作系统

第7章 为什么要有操作系统     35

第8章 任务切换的具体工作     36

8.1 CPU工作原理... 36

8.2 任务切换做的事... 38

第9章 在s3c6410上实现任务切换     39

9.1 了解s3c6410的寄存器... 39

9.2 要用到的几条汇编指令... 41

9.3 在s3c6410上实现任务切换... 45

9.4 在s3c6410上实现简单操作系统... 46

 

 

 

2018-01-05 18:25:26 qq_40334837 阅读数 296
  • 51单片机综合项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

单片机RTOS、ucos操作系统

(1)主要是对内存的管理,ucos的任务(不能叫进程)没有虚拟地址空间映射到物理地址(参考我之前的博客介绍了虚拟、物理地址空间),所有的任务都是一个物理的东西,已经在物理内存上分配好了,他只能静态的把程序修改了重新烧录到物理内存,直接运行。就像以前的非智能手机一样,无法安装软件。

嵌入式、window等高级操作系统区别

(1)这些高级操作系统都有虚拟映射表,对所以的内存都有严格的管控,使用也需要申请,还要释放,所以的进程都在虚拟地址上有空间,真正运行到具体代码段或某个进程时,会建立映射,然后在实际的物理内存上运行,也就是虚拟出4G内存的方式。所以高级操作系统可以动态安装软件,动态删除,就像现在的智能手机,安装后在虚拟地址上分配内存,运行时,在给他分配物理空间。

(2)复杂操作系统运行起来后在软件上分为内核层和应用层,分层后两层的权限不同,内存访问和设备操作的管理上更加精细(内核可以随便访问各种硬件,而应用程序只能被限制的访问硬件和内存地址)。

(3)、复杂操作系统核心功能:
     内存管理。如果没有操作系统,内存是需要程序自己来管理的。譬如在uboot中要使用内存的哪里是自己随便用的,没有注册也没有限制。这时候如果程序自己不小心把同一块内存重复用了就会出现程序逻辑错误。系统大了之后(内存多了)内存管理非常麻烦;有了操作系统之后,操作系统负责管控所有的内存,所有的应用程序需要使用内存时都要向操作系统去申请和注册,由操作系统的内存管理模块来分配内存给你使用,这样好处是可以保证内存使用不会冲突。
    进程调度。操作系统下支持多个应用程序同时运行(所以可以一边聊QQ一边看电影···),这是宏观上的并行。实际上在单核心CPU上微观上是不能并行的,宏观上的并行就是操作系统提供的分时复用机制。操作系统的进程调度模块负责在各个进程之间进行切换。
    硬件设备管理。没有操作系统时要控制任何硬件都要自己写代码,有了操作系统后操作系统本身会去控制各个硬件,应用程序就不用考虑硬件的具体细节了。操作系统的硬件设备管理模块就是驱动模块。
(4)文件系统。文件系统是管理存储设备的一种方式。存储设备是由很多个扇区组成的,每个扇区有512/1024/2048/4096字节,存储设备要以扇区为单位进行读写。如果没有文件系统,程序要自己去读写扇区,就得记得哪个文件在哪个扇区。有了文件系统之后我们人不用再关注扇区,人只用关注文件系统中的目录和文件名,而不用管这个文件在物理磁盘的哪个扇区。

2018-03-20 11:04:22 engineer_wubin 阅读数 1230
  • 51单片机综合项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

源代码和资料网盘链接: https://pan.baidu.com/s/1QG45WgvybpItMGwM0lYLhw 密码: 3pvh

星光操作系统是基于51单片机的嵌入式操作系统,为单片机开发提供软件平台,其特性如下:

(1)   支持AT89S52、STC89C52RC、STC90C58RDPlus、STC12C5A60S2、STC15F2K60S2、STC15W4K32S4、STC8A8K64S4A12和STC8F2K64S4单片机

(2)   支持线程、信号量、屏障和信箱

(3)   采用时间片轮转调度和抢占式调度,时间片为10毫秒

(4)   支持以时钟周期为单位的延迟

(5)   支持中断触发,使用中断系统默认的优先级

(6)   提供组件、库和驱动,方便使用外设

(7)   采用模块化设计,便于裁减和扩充功能

(8)   只有一个文件使用8051汇编语言,其它文件使用C语言,具有良好的可移植性

星光操作系统的开发环境为Keil C51 V9.51a,使用9级优化,重点对大小进行优化,其最小编译结果如下所示:

表1  星光操作系统的最小编译结果

单片机

data

xdata

code

AT89S52

153

0

5133

STC89C52RC(6T模式)

99

57

5582

STC90C58RDPlus(6T模式)

99

57

5582

STC12C5A60S2

99

92

7337

STC15F2K60S2

99

96

8445

STC15W4K32S4

99

127

10773

STC8A8K64S4A12

99

132

12739

STC8F2K64S4

99

99

9259

在调试阶段,使用4级优化,调试通过后使用9级优化。如果9级优化后运行结果错误,增加调用栈和重入栈的容量后重新编译。

星光操作系统支持的组件如下所示:

表2  星光操作系统支持的组件

名称

功能

LCM1601

1601液晶模块

LCM1602

1602液晶模块

LCM12864

12864液晶模块

DS18B20

1线接口温度传感器

DS1302

3线接口实时时钟

HS0038B

红外线解码器

AT24C02

IIC接口EEPROM

AT93C46

3线接口EEPEOM

28BYJ48

步进永磁减速电机

NRF24L01

2.4GHz无线收发器

NRF24L01Plus

2.4GHz无线收发器

MPU6050 6轴运动传感器

下载和解压后,请查看Manual目录中的使用手册,如果遇到问题,欢迎联系。使用手册中提到一些开发板,其中AT89S52、STC89C52RC、STC90C58RDPlus和STC12C5A60S2对应的开发板使用12MHz的无源晶振,其它开发板使用24MHz的内部IRC时钟,Discover 8051 V1 - V7开发板使用相同的原理图。

源代码和资料网盘链接: https://pan.baidu.com/s/1QG45WgvybpItMGwM0lYLhw 密码: 3pvh

2018-04-18 14:47:34 qq_35479392 阅读数 617
  • 51单片机综合项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏
2018-05-14 12:32:38 jiejiemcu 阅读数 4749
  • 51单片机综合项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏
从前面的文章,我们知道,(单核)单片机某一时刻只能干一件事,会造成单片机资源的浪费,而且还有可能响应不够及时,所以,在比较庞大的程序或者是要求实时性比较高的情况下,我们可以移植操作系统。因为这种情况下操作系统比裸机方便很多,效率也高。下面,杰杰将带你们走进FreeRTOS的世界随便看看。

首先说明一下:

①   鉴于作者水平有限,学习并不深入,只是学习了皮毛而已,出错在所难免,欢迎指正。

②   以下文章说的操作系统均为FreeRTOS

③   文章参考了野火的书籍:【 野火®】  《从 0 到 1 教你写 uCOS-III 》   ——刘火良

④   参考了网络开源作者的笔记

下面正式开始本文内容。

在没有用到操作系统之前,单片机的运行是顺序执行,就是说,很多时候,单片机在执行这件事的时候,无法切换到另一件事。这就造成了资源的浪费,以及错过了突发的信号。那么,用上了操作系统的时候,很容易避免了这样的问题。

很简单,从感觉上,单片机像是同时在干多件事,为什么说像呢,因为单片机的执行速度很快,快到我们根本没办法感觉出来,但是同时做两件事是不可能的,在(单核)单片机中,因为它的硬件结构决定了CPU只能在一个时间段做一件事如:

22fb6b385628a1bb63aeb91b2a45e8ad.jpg

如这张图,都是按照顺序来执行这些事的,假设每个任务(事件)的time无限小,小到我们根本没法分辨出来,那么我们也会感觉单片机在同时做这六件事。

真相就是:所有任务都好像在执行,但实际上在任何一个时刻都只有一个任务在执行

 

如是加上了中断系统的话,就可以将上图理解为下图:

1100576-20170518203520978-129299542.png

(图片来源网络)

通常把程序分为两部分:前台系统和后台系统。 简单的小系统通常是前后台系统,这样的程序包括一个死循环和若干个中断服务程序:应用程序是一个无限循环,循环中调用API函数完成所需的操作,这个大循环就叫做后台系统。中断服务程序用于处理系统的异步事件,也就是前台系统。前台是中断级,后台是任务级。简单来说就是程序一直按顺序执行,有中断来了就做中断(前台)的事情。处理完中断(前台)的事情,就回到大循环(后台)继续按顺序执行。

那么问题来了,这样子的系统肯定不是好的系统,我在做第一个任务的时候想做第四个任务,根本做不到啊,其实也能做到,让程序执行的指针cp指向第四个任务就行了。但是任务一旦复杂,那么整个工程的代码的结构,可移植性,及可读性,肯定会差啦。

 FreeRTOS

         那么操作系统的移植就是不可或缺的了。什么叫RTOS?:Real Time OS,实时操作系统,强调的是实时性,就是要规定什么时间该做什么任务。那么假如同一个时刻,需要执行两个或者多个任务怎么办。那么我们可以人为地把任务划分优先级,哪个任务重要,就先做,因为前面一直强调,单片机无法同时做两件事,在某一个时刻只能做一件事。

那么FreeRTOS是怎么操作的呢?先看看FreeRTOS的内核吧:

FreeRTOS是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。 FreeRTOS是用C和汇编来写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS结构简洁,可读性很强!RTOS的内核负责管理所有的任务,内核决定了运行哪个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。

可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务。

1100576-20170518205756744-313403642.png

(图片来源网络)

在FreeRTOS中,每个任务都是无限循环的,一般来说任务是不会结束运行的,也不允许有返回值,任务的结构一般都是

  1. While(1)
  2. {
  3. /****一直在循环执行*****/
  4. }

如果不需要这个任务了,那就把它删除。

移植的教程我就不写了,超级简单的,按照已有的大把教程来做就行了。(如果没有资源,可以在后台找我,我给一份移植的教程/源码)

其实FreeRTOS的运用及其简单,移植成功按照自己的意愿来配置即可,而且FreeRTOS有很多手册,虽然作者英语很差,但是我有谷歌翻译!!!哈哈哈

既然一直都说任务任务,那肯定要有任务啊,创建任务:

  1. // task. h  task.c
  2. BaseType_t xTaskCreate(      TaskFunction_t pvTaskCode,
  3.                               const char * const pcName,
  4.                               uint16_t usStackDepth,
  5.                               void *pvParameters,
  6.                               UBaseType_t uxPriority,
  7.                               TaskHandle_t *pvCreatedTask
  8.                           );

函数的原型都有,按照字面的理解

  1. TaskFunction_t pvTaskCode        //传递进来的是任务函数
  2. const char * const pcName         //传递进来的是任务Name
  3. uint16_t usStackDepth            //传入的是堆栈的大小

在这里要说明一下,在裸机中开发,我们不管局部变量还是全局变量,反正定义了就能用,中断发生时,函数返回地址发哪里,我们也不管。但是在操作系统中,我们必须弄清楚我们的参数是怎么储存的,他们的大小是多大,就需要我们去定义这个堆栈的大小。它就是用来存放我们的这些东西的。太小,导致堆栈溢出,发生异常。(栈是单片机 RAM 里面一段连续的内存空间)

因为在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间。

  1. void *pvParameters              //传递给任务函数的参数
  2. UBaseType_t uxPriority          //任务优先级
  3. TaskHandle_t *pvCreatedTask     //任务句柄

任务句柄也是很重要的东西,我们怎么删除任务也是要用到任务句柄,其实说白了,我操作系统怎么知道你是什么任务,靠的就是任务句柄的判断,才知道哪个任务在执行,哪个任务被挂起。下一个要执行的任务是哪个等等,靠的都是任务句柄。

那么要使用这些东西,我们肯定要实现啦,下面就是实现的定义,要定义优先级,堆栈大小,任务句柄,任务函数等。

  1. //任务优先级
  2. #define LED_TASK_PRIO           2
  3. //任务堆栈大小     
  4. #define LED_STK_SIZE             50
  5. //任务句柄
  6. TaskHandle_t LED_Task_Handler;
  7. //任务函数
  8. void LED_Task(void *pvParameters);

创建任务后,可以开启任务调度了,然后系统就开始运行。

  1. xTaskCreate((TaskFunction_t )LED_Task,    //任务函数
  2.             (const char*    )"led_task",   //任务名称
  3.             (uint16_t       )LED_STK_SIZE, //任务堆栈大小
  4.             (void*          )NULL, //传递给任务函数的参数
  5.             (UBaseType_t    )START_TASK_PRIO, //任务优先级
  6.             (TaskHandle_t*  )&LED_Task_Handler);//任务句柄 
  7.  vTaskStartScheduler();          //开启任务调度

这个创建任务的函数 xTaskCreate 是有返回值的,其返回值的类型是BaseType_t。

我们在描述中看看:

  1. // @return pdPASS if the task was successfully created and added to a readylist, otherwise an error code defined in the file projdefs.h

我们其实可以在任务调度的时候判断一下返回值是否为pdPASS从而知道任务创是否建成功。并且打印一个信息作为调试。因为后面使用信号量这些的时候都要知道信号量是否创建成功,使得代码健壮一些。免得有隐藏的bug。

然后就是具体实现我们的任务LED_Task是在做什么的

当然可以实现多个任务。还是很简单的。

  1. //LED任务函数
  2. void LED_Task(void *pvParameters)
  3. {
  4.     while(1)
  5.     {
  6.         LED0  =  !LED0;
  7.         vTaskDelay(1000);
  8.     }
  9. }

这就是一个简单的操作系统的概述。

下一篇,应该是讲述开启任务调度与任务切换的具体过程。

这个可以参考野火的书籍《从 0 到 1 教你写 uCOS-III》


文章来自“创客飞梦空间”

这是一个专门分享开源方案&心得的公众号,目的是为了记录自己的开发之旅&分享各位大神的创意,把方案,资料,正能量传递给各位开发者,可能,很多人没啥时间在论坛看方案,但是,微信公众号却可以在睡前了解一下新的方案,涉及的有开源的方案,思想,算法,经验,教程等。在未来的日子里,创客飞梦空间与你们同在,共同进步,放飞我们的梦想。


欢迎关注,创客与您一起进步




没有更多推荐了,返回首页