嵌入式系统实习报告

  嵌入式操作系统实习报告

                  ——选题:电子时钟

班级:计本11-3班

姓名:冀慧君

学号:3110717339

同组:吴鹏

指导老师:金红

日期:2014.12.10

一:引言

嵌入式在我们的生活中应用是很广泛的,大到飞机、坦克、导弹,小到手表、遥控器。虽然嵌入式系统是这一、二十年才真正风靡起来的,但是嵌入式这个概念却是很早就存在了,而且现今嵌入式系统的应用数量已经超过了通用计算机,纵观嵌入式的发展历程,我们可以发现其大概可以分为几个阶段:

1、无操作系统阶段

    嵌入式系统最初的应用是基于单片机的,大多以可编程控制器的形式出现的,基本具有检测、伺服、设备指示等功能,但是严格来说都是在没有系统支持的情况下完成的,所以其所能完成的功能有限。

2、简单操作系统阶段

    随着微电子技术工艺水平的提高,电脑制造商开始把嵌入式所需要的硬件比如:串口、I/O接口、微处理器集成到一块芯片上,并开始在简单的操作系统平台上驱动这些硬件。

3、实时操作系统阶段

在20世纪90年代后,计算机的爆炸性增长,带来了操作系统的快速发展,出现了能够运行在各种不同类型的微处理器上的操作系统,具有高度的模块化和拓展性,可以实时的对不同环境做出不同的处理。

4、面向Internet阶段

21世纪网络的快速发展,嵌入式开始应用的各种网络环境当中去,开始出现了嵌入式技术和互联网技术相结合的局面。

那么说了这么多,什么是嵌入式系统呢?根据IEEE(美国电气和电子工程协会)对嵌入式系统的定义是:“用于控制、监视或者辅助操作机器和设备的装置”,它具有可裁剪性、强实时性、统一的接口、良好的移植性等特性,也正是它的这些特性使它应用起来很广范,需要什么就留什么,不需要的就裁剪掉,因此嵌入式技术很灵活,只要确定了需求就可以快速的制造出产品。由此通过刚才纵览嵌入式系统的发展,我们可以很容易看出在可预见的未来,嵌入式技术还有很大的发展空间,而且和我们的日常生活会越来越紧密。因此,开展嵌入式系统综合设计实习,不但可以使我们对嵌入式有一个全面的了解,而且可以培养我们学生利用所学专业知识进行嵌入式开发的能力,同时可以提高我们的动手能力,检验我们的综合能力。

二:嵌入式系统

1.嵌入式系统概述

嵌入式系统是集成电路发展过程中的一个标志性成果,它把计算机直接嵌入到应用系统中,融合了计算机软/硬件技术、通信技术和微电子技术,是一种微电子产业和信息技术产业的最终产品。微电子产业是许多国家优先发展的产业。以超深亚微米工艺和IP核复用技术为支撑的系统芯片技术是国际超大规模集成电路发展的趋势和21世纪集成技术的主流。

2.嵌入式操作系统

嵌入式系统既然是计算机系统,就不可避免地由三大部分构成:CPU、内存和输入输出手段。

(1)可靠性与稳定性对嵌入式系统有着特别重要的意义。

(2)嵌入式系统的软硬件均是面向特定应用对象和任务设计的,具有很强的专用性。

(3)有些嵌入式系统需要长期连续运行(如电话交换机)。

(4)有些要求高可靠的嵌入式系统还需要采用“容错(Fault Tolerance)”技术。

(5)许多嵌入式系统都有实时要求,需要有对外部事件迅速做出反应的能力。

(6)在系统组成上,因为嵌入式系统常常用于控制目的,(

7)与通用计算机相比,嵌入式系统一般都不带用于大容量存储目的的外部设备,也就是不带磁盘。

(8)许多嵌入式系统的人机界面也有其特殊性。

三:了解STC51

(1)工作电压:3.4V-5.5V (5V单片机)/ 2.0V-3.8V (3V 单片机);

(2)工作频率范围:0 -35 MHz,相当于普通8051 的0~420MHz.实际工作频率可达48MHz;

(3)片上集成512 字节RAM;

(4)通用I/O 口(27/23个),复位后为:准双向口/ 弱上拉(普通8051 传统I/O 口)。可设置成四种模式:准双向口/ 弱上拉,推挽/ 强上拉,仅为输入/高阻,开漏。每个I/O 口驱动能力均可达到20mA,但整个芯片最大不得超过55mA;

(5)ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,可通过串口(P3.0/P3.1)直接下载用户程序,数秒即可完成一片;

(6)EEPROM 功能;

(7)看门狗;

(8)内部集成MAX810 专用复位电路(外部晶体20M 以下时,可省外部复位电路);

(9)时钟源:外部高精度晶体/ 时钟,内部R/C 振荡器。用户在下载用户程序时,可选择是使用内部R/C 振荡器还是外部晶体/ 时钟。常温下内部R/C 振荡器频率为:5.2MHz ~6.8MHz。精度要求不高时,可选择使用内部时钟,因为有温漂,请选4MHz ~8MHz;

(10)有2个16 位定时器/ 计数器;

(11)外部中断2 路,下降沿中断或低电平触发中断,Power Down 模式可由外部中断低电平触发中断方式唤醒;

STC51图:

四:了解uCOS-II

(1)uCOSII包括任务调度,时间管理,内存管理,资源管理(信号量,邮箱,消息队列)四大部分,没有文件系统,网络接口,输入输出界面;

(2)它的移植只与4个文件相关:

a)   汇编文件(OS_CPU_A.ASM)

b)   处理器相关C文件(OS_CPU.H,OS_CPU_C.C)

c)   配置文件(OS_CFG.H)

(3)uCOSII有64个优先级,系统占用8个,用户可创建56个任务,不支持时间片

轮转;

(4)uCOSII工作核心原理是:近似地让最高优先级的就绪任务处于运行状态;

(5)操作系统将在下面情况中进行任务调度:

调用API函数(用户主动调用),

中断(系统占用的时间片中断OsTimeTick(),用户使用的中断);

(6)uCOSII(OSCore.c):是操作系统的核心,包括系统初始化,操作系统运行,中断进行的前导,时钟节拍,任务调度,事务处理等多部分。主要任务是维持系统的基本工作;

(7)uCOSII(OSTask.c):解决任务的建立,删除,挂起,回复等等。且uCOSII是以任务为基本单位调度的。

(8)uCOSII(OSTime.c):uCOSII中最小的时钟单位是timetick(时钟节拍).

五:了解1602

(1) 字符型LCD1602通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线VCC(15脚)和地线GND(16脚),其控制原理与14脚的LCD完全一样;

(2)DDRAM:DRAM就是显示数据RAM,用来寄存待显示的字符代码。共80个字节。

(3)CGROM:字符发生存储器,1602已经存储了160个不同的点阵字符图形。

(4)CGRAM:字符发生存储器第一排,可以将自己定制的字符写入在程序需要时取出。

(5)清屏指令:00000001H;

六:焊接材料

材料: STC51开发板一个

1602显示屏--16个接口

万用版一个

5个按钮,5个上拉电阻,七根杜邦线

焊接:使用P0口和P3.5-P3.6-P3.7与1602的接口相连,所以用16口单列插座与上述端口焊接。另外,由于要使用1602显示屏,需将开发板背后右下角的LCD1602焊点焊接。另外,预定5个按键操作时钟,选定P2.3-P2.7为端口进行操作,则用单列插针与其焊接,以便连接杜邦线。最后用九个单列插针与电源口焊接。

万用板焊接电路图如下:

万用板实物图如下:

七:往STC51开发板移植uCOSII

(1)修改OS_CPU.H

--将BOOLEAN要定义成unsigned char 类型,因为bit类型为C51特有,不能用在结构体里.(typedef unsigned char  BOOLEAN; )

--将开关中断方式改为EA=1,EA=0;(

#define  OS_ENTER_CRITICAL()  EA=0

#define  OS_EXIT_CRITICAL()   EA=1)

--由于51堆栈自低地址往高地址生长,所以堆栈增长方向的设置必须修改。(#define  OS_STK_GROWTH    0)

--因为MCU-51没有软中断指令,所以用程序调用代替。

(#define  OS_TASK_SW()     OSCtxSw())

(2)修改OS_CPU_C.C

--用C语言在OS_CPU_C.C中添加钩挂函数

初始化任务堆栈函数:OS_STK *OSTaskStkInit (void (*task)(void *pd) reentrant, void *ppdata, OS_STK *ptos, INT16U opt)  reentrant;

任务创建钩挂函数:void OSTaskCreateHook (OS_TCB *ptcb) reentrant;

任务删除钩挂函数:void OSTaskDelHook (OS_TCB *ptcb) reentrant;

任务切换钩挂函数:void OSTaskSwHook (void) reentrant;

任务钩挂函数:void OSTimeTickHook (void) reentrant;

--另外(1),KEIL缺省情况下编译的代码不可重入,而多任务系统要求并发操作导致重入,所以要在每个C函数及其声明后标注reentrant关键字.

--另外(2),"pdata","data"在uCOS中用做一些函数的形参,但它同时又是KEIL的关键字,会导致编译错误,我通过把"pdata"改成"ppdata","data"改成"ddata"解决了此问题.

(3)修改OS_CPU_A.ASM

--运行优先级最高的就绪任务OSStartHighRdy();

--任务级的任务切换函数OSCtxSW();

--中断级的任务切换函数OSIntCtxSw();

--时钟节拍中断服务函数OSTickISR();

八:验证移植的成功性,编写main函数在外部做出特征

STC51开发板上P5.5端口控制开发板上LED灯亮灭,当P5.5低电平是灯灭,当P5.5高电平时灯亮。则在main函数中添加一个控制灯亮的任务即可验证实验成功与否。

void delayms(unsigned int m)        //延时程序

  {

     int  a, b;

     for(a=0;a<5000;a++)

     for(b=0;b<m;b++);  

 }

void main(void){

        while(1){             //将控制放到循环中,使其循环灯灭,

                P55=0;         //更容易观察

                delayms(50);

                P55=1;

                delayms(50);

        }}

若实验结果为LED显示灯亮暗时间相同,则实验成功。可更改delayms()z中的参数来控制灯亮暗时间。

九:在1602LED显示屏上显示字符

1602上rs端口控制写入的是数据还是命令,当rs=0时写入命令,当rs=1时写入数据。另外1602上r/w端口控制写还是读,在本实验中一直置0。并在传输数据时开中断,延时后关中断。由此可编写两个函数:

void  lcd_wmc(uchar i)       //向lcd中写命令rs=0

void  lcd_wmd(uchar i)       //想lcd中写代码rs=1

另外,对于lcd的初始化可以单独写为一个函数,包括开启显示屏,清屏等四个命令,用延时程序相隔以确保每个命令执行完成。

在主程序中创建一个任务,提前开辟出堆栈区,将所要显示的字符存入数组。若显示的字符未在CGROM中找到,则可在lcd初始化时用循环写入CGRAM中,CGRAM有16个空的位置可以存放。本测试显示0-9阿拉伯数字,可直接将0x30-0x39使用lcd_wmd()函数写入到LED显示屏中。

十:设计时钟程序

思路:使用定时器一进行计数。

a>在main函数中开启定时器一开始计数ET1=1;

b>计算出计算初值。STC-ISP中选择振荡器放大增益为11.0592MHz,为了计算方便约为12HMz。机器周期T=12/主振频率=1微秒。在程序中设计时次数20次时为1秒,则一次为0.05秒。0.05秒/1微秒=50000次,计时器一工作在方式一可计65536次,需求为50000次,则计数初值为65536-50000=15536次。换算成16进制数则为3CB0。

c>编写定时器一的中断函数void time0() interrupt 3;设好计算初值,每当count达到20次则sec+1且进入判断,若sec>59则min+1&&sec=0,若min>59则hou+1&&min=0。并且每当sec,min,hou改变时调用自己编写的lcd_time()函数刷新时间显示区域。

d>编写时钟按键控制程序:将判断放入死循环中,使其可以随时察觉外界动作。进入判断,当key(x)低电平时,则调用相应函数key(x)_fun();

十一:同时进行三个任务

Task1:控制LED灯亮灭

Task2:控制1602显示屏上半行显示时间

Task3:控制1602显示屏下半行显示字幕并移动

先在main()函数中调要OSTaskCreate()函数创建三个任务,并制定优先级。然后调用uCOS-II的延时函数OSTimeDlyHMSM()让三个任务交替进行。

十二:总结

1.任务的分配

本次实验选题LED屏显示时钟应用,在跟同学经过仔细讨论后,各自选择了自己擅长的方面入手,他负责嵌入式操作系统的移植,我负责软件的编写。两个人在实习期间相互了解进况,一起解决各自遇到的难题,脱离了以前的编程式个人英雄主义,效率和成果显著提高。

2.实习流程

在我进行软件的编写时,先了解了uCOS-II系统的任务是如何调度和进行的,然后结合C语言编程,先后实现了对LED屏的显示控制和时钟的时间计算。

在主函数中调用1602的初始化函数和uCOS-II的初始化函数,然后分别将所写的三个任务用uCOS-II的创建函数创建并开始运行。在每个任务中调用uCOS-II的延时函数即可让三个任务交替进行。

3.遇到的问题

一开始阅读uCOS-II代码时,总是为了查找当前所调用的函数函数体找的焦头烂额,后来在同学告知下,得知Keil可以按快捷键F12直接跳到函数的定义位置,方便不少。

在初始化1602显示屏的函数编写时,每次将开发板断电在接电后,1602屏幕总是在字符处存在阴影。在经过一系列探讨和查询资料后,发现原来是1602没有彻底完成初始化,在将初始化指令重写三遍并在每个指令后都加一个延时程序后,阴影消除。

设计自定义的延时函数时,不知如何准确的设计延时时间,后来在网上查询到C51在一个机器周期可以执行十二条指令,这样延时一秒即是50000×12,即可以将延时控制到一个精准度。

任务调度的时候,又遇到如何将三个任务同时进行的问题,因为uCOS-II总是执行最高优先级的任务,后来在网上查到uCOS-II有一个系统延时函数,可以将当前任务挂起若干秒去执行下一个当前最高优先级的任务,这样任务交替执行就解决了。

十三:主函数代码

Lcd.h------本文件中定义按键,编写1602初始化函数,向1602写入指令和数据函数,以及自己定义的时钟中断函数和1602初始时显示所需字符的函数。

#ifndef __LCD_H__

#define __LCD_H__

             /*11111111111111111电子时钟的宏定义111111111111*/                                                    

#define uchar unsigned char

uchar code dislcd[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x65,0x69,0x6d,0x54};

unsigned char code d[] = {"PDS"};

sbit rs=P3^5;

sbit rw=P3^6;

sbit e=P3^7;

sbit  key1=P2^3; //key1、key5可以调整分针 ,key2、key4可以调整时针。key3为复位键

sbit key2=P2^4;

sbit key3=P2^5;

sbit key4=P2^6;

sbit key5=P2^7;

unsigned int cou,sec,min,hou;

unsigned int i,j,m,n;

void delay() //  延时子函数      功能:延时

{

      unsigned int j;

      for(j=0;j<255;j++);

}

void delay1()    // 延时子函数      功能:用于按键延时

{

      unsigned int a,j;

      for(a=0;a<20;a++)

            for(j=0;j<160;j++);

}

void lcd_wmc(uchar i) //通过数据口向液晶写命令

{

      P0=i;

      rs=0;

      rw=0;

      e=0;

      delay();

      e=1;

}

void lcd_wmd(uchar i)  //通过数据口向液晶写数据

{

      P0=i;

      rs=1;

      rw=0;

      e=0;

      delay();

      e=1;

}

 void lcd_time()   //  函数名:时间显示//   功能:在液晶第一行显示时间

{

      lcd_wmc(0x80);

      lcd_wmd(dislcd[14]);

      lcd_wmc(0x81);

      lcd_wmd(dislcd[12]);

      lcd_wmc(0x82);

      lcd_wmd(dislcd[13]);

      lcd_wmc(0x83);

      lcd_wmd(dislcd[11]);

      lcd_wmc(0x84);

      lcd_wmc(0x85);

      lcd_wmd(dislcd[hou/10]);

      lcd_wmc(0x86);

      lcd_wmd(dislcd[hou%10]);

      lcd_wmc(0x87);

      lcd_wmd(dislcd[10]);

      lcd_wmc(0x88);

      lcd_wmd(dislcd[min/10]);

      lcd_wmc(0x89);

       lcd_wmd(dislcd[min%10]);

      lcd_wmc(0x8a);

      lcd_wmd(dislcd[10]);

      lcd_wmc(0x8b);

      lcd_wmd(dislcd[sec/10]);

      lcd_wmc(0x8c);

      lcd_wmd(dislcd[sec%10]);

      lcd_wmd(0x8d);

}

void lcd_init()    //1602液晶初始化

{

       lcd_wmc(0x38);

       delay();

       lcd_wmc(0x38);

       delay();

       lcd_wmc(0x38);     //8位数据,双列,5*7字形

       delay();

       lcd_wmc(0x0c);        //开启显示屏,关光标,光标不闪烁

       delay();

       lcd_wmc(0x06);        //显示地址递增,即写一个数据后,显示位置右移一位

       delay();

       lcd_wmc(0x01);        //清屏

       delay1();

}

void time0() interrupt 3 // 函数名:定时器1中断函数    //功能:时间调整

{

      TH1=0x3C;

      TL1=0xB0;

      cou++;

      if(cou>=20)

      {

            cou=0;

            sec++;

            lcd_time();

            if(sec>59)

            {

                  sec=0;

                  min++;

                  lcd_time();

                  if(min>59)

                  {

                  min=0;

                  hou++;

                  lcd_time();

                  if(hou>23)

                  hou=0;

                  lcd_time();

                  }

            }

      }    

}

#endif

 

Main.c--主函数所在的C文件,主要是编写三个任务和程序的入口函数,给三个任务初始化了三个栈,然后完善三个任务的功能。

#include "includes.h"

#include "lcd.h"

OS_STK xdata Task1Stk[MaxStkSize+1];//TCB结构体中OSTCBStkPtr总是指向用户堆栈最低地址,该地址空间内存放用户堆栈长度,其上空间存放系统堆栈映像

OS_STK xdata Task2Stk[MaxStkSize+1];//即:用户堆栈空间大小=系统堆栈空间大小+1

OS_STK xdata Task3Stk[MaxStkSize+1];

//OS_EVENT* xdata FirstSem;

INT8U err;

 void key1_fun()

{

      delay1();    //键值有效,等待key1弹起

                        if(key1==1) //key1弹起,分针加

                        {

                               min++;

                               if(min>59)

                               {

                                     min=0;

                                     hou++;

                               }

                              lcd_wmc(0x88);

                              lcd_wmd(dislcd[min/10]);

                              lcd_wmc(0x89);

                              lcd_wmd(dislcd[min%10]);

                  }

}

void key2_fun()

{

                         delay1();

                        if(key2==1)

                        {

                               hou++;

                               if(hou>23)

                               hou=0;

                              lcd_wmc(0x85);

                              lcd_wmd(dislcd[hou/10]);

                              lcd_wmc(0x86);

                              lcd_wmd(dislcd[hou%10]);

                         }

}

void key3_fun()

{

                        delay1();

                        if(key3==1)

                        {

                              hou=0;

                              lcd_wmc(0x85);

                              lcd_wmd(dislcd[0]);

                              lcd_wmc(0x86);

                              lcd_wmd(dislcd[0]);

                              min=0;

                              lcd_wmc(0x88);

                              lcd_wmd(dislcd[0]);

                              lcd_wmc(0x89);

                              lcd_wmd(dislcd[0]);

                              sec=0;

                              lcd_wmc(0x8b);

                              lcd_wmd(dislcd[0]);

                              lcd_wmc(0x8c);

                              lcd_wmd(dislcd[0]);

                        }

}

void key4_fun()

{

                              delay1();   

                        if(key4==1)

                         {

                               if(hou>0)

                              {

                                      hou--;

                               }   

                         if(hou < 1)

                              {    

                                    hou = 0;

                                    lcd_wmc(0x88);

                                    lcd_wmd(dislcd[min/10]);

                                    lcd_wmc(0x89);

                                    lcd_wmd(dislcd[min%10]);

                              }

                         }

}

void key5_fun()

{

            delay1();    //键值有效,等待key5弹起

                  if(key5==1) //key5弹起,分针减

                  {

                         if(min > 0)

                         {

                              min--;

                         }

                        if(min ==0)

                        {    

                              min = 0;

                              lcd_wmc(0x88);

                              lcd_wmd(dislcd[min/10]);

                              lcd_wmc(0x89);

                              lcd_wmd(dislcd[min%10]);

                        }

                  }

}

 void Task1(void *ppdata)   reentrant

{

      ppdata = ppdata;

      ET0 = 1;       //根任务开时钟节拍中断 重要!!!!!!!!!!!!!!!!!!!!!!!!

               while(1)

               {

                     OSTimeDlyHMSM(0,0,1,0);

                   while(1)        //按键程序

                  { OSTimeDlyHMSM(0,0,0,250);

                        if(key1==0)   //键值key1是否有效

                        {       OSTimeDlyHMSM(0,0,0,250);

                         key1_fun();

                        }

                        if(key2==0)

                        {       OSTimeDlyHMSM(0,0,0,250);

                                key2_fun();

                        }

                        if(key3==0)

                        {       OSTimeDlyHMSM(0,0,0,250);

                                key3_fun();

                        }   

                         if(key4==0)      

                        {           OSTimeDlyHMSM(0,0,0,250);

                              key4_fun();

                        }

                  if(key5==0)       //键值key5是否有效

                        {           OSTimeDlyHMSM(0,0,0,250);

                              key5_fun();

                         }

                    }

                    }

}

void Task2(void *ppdata) reentrant

{

      j = 0;    

      while(1)

      {    

                  OSTimeDlyHMSM(0,0,1,0);    

                i=0;

                  lcd_wmc(0x80+0x40+i+j-1);

                  lcd_wmd(0x20);

       while(d[i]!='\0')         //显示字母

      {                             

             lcd_wmc(0x80+0x40+i+j);

             lcd_wmd(d[i]);

             i++;           

      }    

      j++;

      if(j > 16)

            j = 0;

      }    

}

void delayms(unsigned int m)

  {

       int  a, b;

       for(a=0;a<5000;a++)

       for(b=0;b<m;b++);      

 }

void Task3(void *ppdata) reentrant

{

while(1)

      {    

            OSTimeDlyHMSM(0,0,1,0);

            while(1)

                  {          

                        P55=0;

                        delayms(50);

                        P55=1;

                        delayms(50);

                  }

            }

}

void main(void)

{

       P0=0xff;

       cou=0;    //计算计数次数

     sec=00;   //秒

     min=00;

       hou=00;

     TMOD=0x10;

     TH1=0x3C;

     TL1=0xB0;

     EA=1;

     ET1=1;

     TR1=1;

       lcd_init();

     OSInit();

       lcd_time();

      InitHardware();

      OSTaskCreate(Task1,(void*)0,&Task1Stk[0],1);

      OSTaskCreate(Task2,(void*)0,&Task2Stk[0],2);

      OSTaskCreate(Task3,(void*)0,&Task3Stk[0],3);

      OSStart();

}

参考文献:    嵌入式系统---百度百科

从零开始移植ucos-II_最有用---野火嵌入式开发工作室

教你如何把UCos-ii_在STM32上的移植---CSDN

1   602液晶屏的引脚定义--CSDN

相关推荐