0

关于投票
KEIL C51中文教程
rar
系统分类: 单片机
用户分类: 单片机与编程
标签: 无标签
来源: 无分类
发表评论 阅读全文(950) | 回复(2)

0

关于投票
硬件堆栈和软件堆栈

      最近在很多地方看到有硬件堆栈和软件堆栈的问题。发现自己对这个问题也很模糊,所以就去查了相关的一些资料,在这里谈谈我个人的理解。

      硬件堆栈:或许也可以称作系统堆栈,是位于片内RAM区。有人说,只要能使用PUSH,POP指令的单片机,都可以说含有硬件堆栈。这样的说法我个人觉得不是很全面。通过指令进行压栈和出栈操作只是系统堆栈中的一种操做。系统堆栈还可以被隐含调用。例如,当调用子程序时,系统会主动把断点压入堆栈,并不需要用户通过指令操作。系统堆栈可以用来保存数据,或在任务子程序间传递数据。

    软件堆栈:也可以说是用户堆栈。可以被定义在内部或外部 RAM中。它是用户为任务建立的专用数据堆栈,与系统堆栈的数据区是隔开的。它可以保存用户想保存的任何寄存器和状态字。

     在嵌入式系统的移植中,理解移植对象的堆栈结构,有着重要的意义。

     这是我个人的理解,不代表我的理解就是正确的,欢迎大家指正!

系统分类: 单片机
用户分类: 单片机与编程
标签: 无标签
来源: 原创
发表评论 阅读全文(713) | 回复(1)

0

关于投票
51单片机操作系统开发

       51系列单片机是美国Intel公司在1980年推出的高性能8位单片机,在我国的应用非常广泛。目前,在软件设计中需要软件工程师从底层做起,在系统软件设计方面需要做大量的重复性劳动。如果开发一套基于51系列单片机的操作系统,那么用户只需要编写各个任务的程序,不必同时将所有任务运行的各种情况记在心中,不但大大减少了程序编写的工作量,而且减少了出错的可能性。1 开发平台的选择和论证  开发平台的选择至关重要,因为有时它不光影响进度、产品质量、可维护性等一般问题,还涉及到方案的可实现性。  

在本系统中,选择51系列单片机作为操作系统的运行平台有以下原因。  首先,51系列单片机应用非常广泛,一大批性能优越的51兼容单片机相继推出。这里包括:低功耗、高速度和增强型的Philips公司的系列产品;完美地将Flash(非易失闪存技术)EEPROM与80C51内核结合起来的Atmel公司的系列产品;在抗干扰性能,电磁兼容和通信控制总线功能上独树一帜,其产品常用于工作环境恶劣场合的Siemens公司的系列产品以及一些其它公司的产品。既然产品如此丰富,性能如此优越,那么在处理多任务并且对实时性要求严格的系统设计中,为了充分挖掘单片机的潜能(尤其是在实时性方面),也是为了简化开发的过程,基于51系列单片机的实时操作系统的需求就十分强烈了。Keil公司的RTX51 Full就是一个基于51系列单片机的有实用价值的实时操作系统,但该操作系统是一个源码不公开的收费软件。  

其次,借助于Keil C51的集成开发环境,完全能开发出适用于51系列单片机的操作系统代码。Keil C51软件供给丰富的库函数和功能强大的Windows界面集成开发调试工具。  另外重要的一点, Keil C51生成的目标代码效率非常高,多数语句生成的汇编代码很紧凑,不难理解。在开发大型软件时,更能体现高级语言的优势。C编译器能产生可重入代码,而且用C语言能打开和关闭中断。2 开发51单片机操作系统应注意的问题(1)操作系统软件的代码不能太长  因为51系列单片机的系统硬件资源相对匮乏,如果操作系统的代码比应用程序的代码还大,甚至使得用户的应用程序要考虑给操作系统让出资源,这样的操作系统即使功能再完善,也不实用。现在流行的嵌入式操作系统就不能应用于51系列单片机,原因是代码太大。开发一个5000行的基于裸机的应用程序也就是占用7~8KB ROM空间,一个操作系统用掉了几十KB,占空间不算,实时性的优势恐怕也没了(执行这么多的指令要时间)。所以,μCOS的作者也不支持将他的代码移植到51系列单片机上,这也就不奇怪了。(2)操作系统不能占用太多的片内RAM空间  51系列单片机只有128个或者256个字节的片内RAM空间,稍微不注意就用完了。如果操作系统把片内的RAM使用得所剩无几,那用户的应用程序用什么? 如果说用户的程序能把变量定义在片外RAM中的话,那么系统的硬件堆栈放在哪? 众所周知,51系列单片机的硬件堆栈不能放在片外,所以要在51系列单片机上开发操作系统的话就要少用它的片内RAM。但是不用片内RAM是办不到的,因为操作系统也要传递参数,也要使用堆栈。C51单片机的C函数传递参数是通过寄存器和存储器的,不能通过堆栈。但是能通过一些措施使得操作系统代码少用片内RAM。(3)解决好函数的重入问题  开发实时占先式的操作系统,可重入函数是非用不可的。可重入函数能被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都能被中断,一段时间后又能运行,而应用数据不会丢失。使得函数具有可重入性必须使得函数能够满足下列三个条件之一:① 不使用共享资源;② 在使用共享资源时关中断,使用完毕后再开中断;③ 在使用共享资源时申请信号量,使用完后释放信号量。   这些条件在标准C中编程很不难实现,但是在Keil C51中就比较麻烦。因为标准C是把局部变量分配到用户堆栈中(动态分配),而Keil C51将局部变量分配到寄存器或内存固定地址(静态分配),并通过变量覆盖分析的方法,使多个函数的局部变量使用相同的内存地址以减少内存占用。在Keil C51中,如果局部变量分配在寄存器中还好些,如果局部变量分配在内存中就比较麻烦。(4)堆栈的分配问题  占先式操作系统的主要任务就是进行任务的调度,通过对任务的实时调度来完成系统的功能。任务调度过程中,不可避免的发生任务对系统资源的抢占问题,因为系统中CPU只有一个,而每个任务都认为自己是CPU的绝对占用者,每一个任务都是一个死循环。任务间进行切换的依据就是各自的优先级,一个高优先级的任务能通过任务调度函数或者中断退出函数等来中止正在运行的任务。被中断的任务只有自己的优先级在当前就绪任务表中最高时,才能从被中断处继续运行。这就需要为每个任务分配任务堆栈,来保存任务的环境变量。由于每个任务在不一样时刻被中断时需要保存的环境变量数目不一样,所以任务堆栈空间的分配问题也是一门学问。3 一些解决问题的技巧(1)片内RAM占用问题的解决  任务堆栈最好不要放在片内,如果把任务堆栈放在片内的话,用户应用程序可使用的资源就非常有限,应用程序的功能也会受到限制。这就是为什么某些把任务堆栈放在片内的基于51系列单片机的实时操作系统只能用来做些演示实验,但并不实用。一个有实用价值的基于51系列单片机的实时操作系统必须在512字节以上的RAM环境中运行。随着集成技术的发展,现在已经出现了很多带有辅助RAM的51系列单片机,这类单片机把片外的RAM集成到芯片内,使用MOVX指令来访问这些RAM。如果用户不想通过三总线来扩展片外RAM的话,能选用这种带有辅助RAM的单片机。此外,因为操作系统要用到一些全局变量,鉴于处理的速度问题又不想把它们全部的放在片外,那就能根据这些全局变量应用的频繁程度来决定把哪些移到片外,哪些留在片内。别小看这几个字节的节约,在51系列单片机上效果会很明显。笔者认为在这种资源相对匮乏的单片机上,开发操作系统的最高境界应该是开发一个绿色的操作系统,用户在应用操作系统时能用的系统资源应该和基于裸机编程差不多。(2)重入问题的解决  应该尽量使有重入性要求的函数的参数传递通过寄存器来完成,这样能用一般的方法来编写函数,使得函数具有重入性。如果实在是寄存器不够用的话,能动用硬件堆栈来保存这些局部变量。(3)堆栈分配问题的解决  鉴于各个任务对于任务堆栈大小的要求不一样,即使同一个任务在不一样的时刻被中断,它对堆栈大小的要求也不相同的情况,能将任务堆栈多分配出一个字节,用来统计任务堆栈中有效数据的个数。单片机的片内RAM中,堆栈的栈底也做一个标志,当任务切换时,把当前任务放在堆栈中的环境变量从栈底到栈顶全部拷贝到任务的堆栈中,然后把将要运行任务的任务堆栈中的所有数据恢复到栈底标志开始的地方。任务堆栈和硬件堆栈之间的数据拷贝如图1所示。            

其中,Stack(i)和Stack(j)都是指针数组Stack[max_tasks]中的元素,NUM=SP-StkStart,图1中所要进行的操作步骤是:①将系统硬件堆栈中的内容放到当前任务的堆栈中;②把将要运行的任务的堆栈内容移到系统的硬件堆栈中,并将硬件堆栈中的内容弹出到各个寄存器。这个过程就完成了任务的切换。结 语  本文介绍了在基于51系列单片机的嵌入式操作系统开发中,可能遇到的几个问题和它们的解决办法。这些想法都是笔者在学习和实践中得来的,相信能够对从事相同工作的人员有一定启发。 

系统分类: 单片机
用户分类: 单片机与编程
标签: 无标签
来源: 无分类
发表评论 阅读全文(424) | 回复(0)

0

关于投票
C167 IO口模拟串口程序

   最近写了一个C167的IO口模拟串口程序,说是写,其实是参考了别人写的51程序,然后自己作了一些改动。本来51程序中的用中断置标志位,发现在167中不好用,所以就改用查询方式,效果也一样。

   程序如下:

/**********************************************

C167 IO 口模拟串口异步通讯程序

使用查询方式的C程序 占用定时器T3

**********************************************/
#include"reg167.h"


sbit DP3_8 = DP3^8;
sbit DP4_7 = DP4^7;
sbit BT_SND =P4^7;
sbit BT_REC =P3^8;

void PSendChar(unsigned char inch)
{
unsigned char ii;
ii=0;
T3IR=0;
BT_SND=0; //start bit
T3R=1;//启动
while(!T3IR);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
T3IR=0;
while(!T3IR);
ii++;
inch>>=1;
}
BT_SND=1;
T3IR=0;
while(!T3IR);
T3R=0; //停止
}

//接收一个字符
unsigned char PGetChar()
{
unsigned char rch,ii;
T3R=1;
T3IR=0;
ii=0;
rch=0;
while(!T3IR); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
T3IR=0;
while(!T3IR);

}
T3IR=0;
while(!T3IR)
{
if(BT_REC)
{
break;
}

}
T3R=0; //停止timer
return rch;
}
//检查是不是有起始位
bit StartBitOn()
{
return  (BT_REC==0);

}
void main()
{
unsigned char gch;
DP3_8 = 0;
DP4_7 = 1;

T3CON=0x0002; //T3工作在定时方式,输入频率为625KHZ,加计数
T4CON=0x0027;//T4工作在重装载方式
T3R=0; //在发送或接收才开始使用
T3IR=0;
T3=65471;
T4=65471;
T3IE=1;
IEN=1;

PSendChar(0x55);
PSendChar(0xaa);
PSendChar(0x00);
PSendChar(0xff);

while(1)
{
if(StartBitOn())
{
gch=PGetChar();
PSendChar(gch);
}
}

}

附51模拟串口程序---------------------------------------------------------------------------------------------------

51 IO口模拟串口通讯C源程序
[ 2007-8-14 11:58:00 | By: lovesam1983 ]
 
#i nclude
sbit BT_SND =P1^0;
sbit BT_REC =P1^1;
/**********************************************

IO 口模拟232通讯程序

使用两种方式的C程序 占用定时器0

**********************************************/

#define MODE_QUICK

#define F_TM F0

#define TIMER0_ENABLE  TL0=TH0; TR0=1;
#define TIMER0_DISABLE TR0=0;

sbit ACC0=   ACC^0;
sbit ACC1=   ACC^1;
sbit ACC2=   ACC^2;
sbit ACC3=   ACC^3;
sbit ACC4=   ACC^4;
sbit ACC5=   ACC^5;
sbit ACC6=   ACC^6;
sbit ACC7=   ACC^7;

void IntTimer0() interrupt 1
{
F_TM=1;
}
//发送一个字符
void PSendChar(unsigned char inch)
{
#ifdef MODE_QUICK
ACC=inch;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

BT_SND=ACC0; //先送出低位
F_TM=0;
while(!F_TM);

BT_SND=ACC1;
F_TM=0;
while(!F_TM);

BT_SND=ACC2;
F_TM=0;
while(!F_TM);

BT_SND=ACC3;
F_TM=0;
while(!F_TM);

BT_SND=ACC4;
F_TM=0;
while(!F_TM);

BT_SND=ACC5;
F_TM=0;
while(!F_TM);

BT_SND=ACC6;
F_TM=0;
while(!F_TM);

BT_SND=ACC7;
F_TM=0;
while(!F_TM);

BT_SND=1;
F_TM=0;
while(!F_TM);


TIMER0_DISABLE; //停止timer
#else
unsigned char ii;

ii=0;

F_TM=0;
BT_SND=0; //start bit
TIMER0_ENABLE; //启动
while(!F_TM);

while(ii<8)
{
if(inch&1)
{
BT_SND=1;
}
else
{
BT_SND=0;
}
F_TM=0;
while(!F_TM);
ii++;
inch>>=1;
}
BT_SND=1;
F_TM=0;
while(!F_TM);

#endif
TIMER0_DISABLE; //停止timer
}
//接收一个字符
unsigned char PGetChar()
{
#ifdef MODE_QUICK

TIMER0_ENABLE;
F_TM=0;
while(!F_TM); //等过起始位
ACC0=BT_REC;

TL0=TH0;

F_TM=0;
while(!F_TM);
ACC1=BT_REC;

F_TM=0;
while(!F_TM);
ACC2=BT_REC;

F_TM=0;
while(!F_TM);
ACC3=BT_REC;

F_TM=0;
while(!F_TM);
ACC4=BT_REC;

F_TM=0;
while(!F_TM);
ACC5=BT_REC;

F_TM=0;
while(!F_TM);
ACC6=BT_REC;

F_TM=0;
while(!F_TM);
ACC7=BT_REC;

F_TM=0;

while(!F_TM)
{
if(BT_REC)
{
break;
}
}
TIMER0_DISABLE; //停止timer
return ACC;
#else
unsigned char rch,ii;
TIMER0_ENABLE;
F_TM=0;
ii=0;
rch=0;
while(!F_TM); //等过起始位

while(ii<8)
{
rch>>=1;
if(BT_REC)
{
rch|=0x80;
}
ii++;
F_TM=0;
while(!F_TM);

}
F_TM=0;
while(!F_TM)
{
if(BT_REC)
{
break;
}

}
TIMER0_DISABLE; //停止timer
return rch;

#endif

}
//检查是不是有起始位
bit StartBitOn()
{
return  (BT_REC==0);

}
void main()
{
unsigned char gch;

TMOD=0x22; /*定时器1为工作模式2(8位自动重装),0为模式2(8位
自动重装) */
PCON=00;

TR0=0; //在发送或接收才开始使用
TF0=0;
TH0=(256-96); //9600bps 就是 1000000/9600=104.167微秒 执行的
timer是
//            
104.167*11.0592/12= 96
TL0=TH0;
ET0=1;
EA=1;

PSendChar(0x55);
PSendChar(0xaa);
PSendChar(0x00);
PSendChar(0xff);

while(1)
{
if(StartBitOn())
{
gch=PGetChar();
PSendChar(gch);
}
}

}
 

   

系统分类: 单片机
用户分类: 单片机与编程
标签: 无标签
来源: 无分类
发表评论 阅读全文(347) | 回复(0)
总共 , 当前 /