2019-09-22 23:12:03 qingwufeiyang12346 阅读数 139

本系列教程以AVR单片机为对象,介绍单片机的快速开发方法。

参考教材:《单片机技术及应用项目教程》 栾秋平 电子工业出版社 2019.6 第1版

本文介绍应用层次化软件设计思想实现多路LED闪烁的方法。

一、认知层次化软件设计思想

二、将硬件原理图直译成映射层

#ifndef HAL_H_
#define HAL_H_

//引脚定义
/////////////////////////////////////////////////////////////////////
//PortB
#define IO_PB_DIR 	0b00000000
#define IO_PB_OUT 	0b00000000			
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
//PortC
#define IO_PC_DIR 	0b00000000
#define IO_PC_OUT 	0b00000000		
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
//PortD
#define IO_PD_DIR 	0b11111111
#define IO_PD_OUT 	0b11111111
/////////////////////////////////////////////////////////////////////
#define LED0 BIT0
#define LED1 BIT1
#define LED2 BIT2
#define LED3 BIT3
#define LED4 BIT4
#define LED5 BIT5
#define LED6 BIT6
#define LED7 BIT7

#define LED0_OPEN CLRBIT(PORTD, LED0)
#define LED0_SHUT SETBIT(PORTD, LED0)
#define LED1_OPEN CLRBIT(PORTD, LED1)
#define LED1_SHUT SETBIT(PORTD, LED1)
#define LED2_OPEN CLRBIT(PORTD, LED2)
#define LED2_SHUT SETBIT(PORTD, LED2)
#define LED3_OPEN CLRBIT(PORTD, LED3)
#define LED3_SHUT SETBIT(PORTD, LED3)
#define LED4_OPEN CLRBIT(PORTD, LED4)
#define LED4_SHUT SETBIT(PORTD, LED4)
#define LED5_OPEN CLRBIT(PORTD, LED5)
#define LED5_SHUT SETBIT(PORTD, LED5)
#define LED6_OPEN CLRBIT(PORTD, LED6)
#define LED6_SHUT SETBIT(PORTD, LED6)
#define LED7_OPEN CLRBIT(PORTD, LED7)
#define LED7_SHUT SETBIT(PORTD, LED8)

#define ALL_LED_OPEN PORTD = 0x00
#define ALL_LED_SHUT PORTD = 0xff
//函数原型声明

#endif /* HAL_H_ */

三、逻辑层设计

int main(void)
{
	//定义局部变量
	
	//目标板初始化,该函数会自动初始化相应的外设文件	
	TARGET_Init();
	
	//初始化全局变量	
		
	//在上电时,执行的相应操作	
			
	//后台主循环
	while(1)
	{
		/*
		**********************************
		在这里完成自己的项目逻辑
		**********************************
		*/	
		LED0_OPEN;
		LED7_OPEN;
		TARGET_Delayms(200, 1);
		LED1_OPEN;
		LED6_OPEN;
		TARGET_Delayms(200, 1);
		LED2_OPEN;
		LED5_OPEN;
		TARGET_Delayms(200, 1);
		LED3_OPEN;
		LED4_OPEN;
		TARGET_Delayms(200, 1);		
		ALL_LED_SHUT;
		TARGET_Delayms(200, 1);				
		/*
		**********************************
		喂狗语句,大部分工程项目都不应去除
		**********************************
		*/	
		#if INTERNAL_PERIPHERAL_WDT_MODE != 0
			TARGET_WatchDogReset();
		#endif
	}
	return 0;	//永不执行
}

 

 

任何问题,只需在此文章的评论处留言即可,我将尽力解答,不要试图采用其它的联系方式,我一概不理会。

原创性文章,转载请注明出处CSDN:http://blog.csdn.net/qingwufeiyang12346。

 

 

 

 

 

 

 

2011-12-07 11:46:32 akaedu_cd 阅读数 359
    嵌入式系统设计主要有三个层次,具体如下:

   1. 第1层次:以PCB CAD软件和ICE为主要工具的设计方法。

  这是过去直至现在我国单片机应用系统设计人员一直沿用的方法,其步骤是先抽象后具体。

  抽象设计主要是根据嵌入式应用系统要实现的功能要求,对系统功能细化,分成若干功能模块,画出系统功能框图,再对功能模块进行硬件和软件功能实现的分配。

  具体设计包括硬件设计和软件设计。硬件设计主要是根据性能参数要求对各功能模块所需要使用的元器件进行选择和组合,其选择的基本原则就是市场上可以购买到的性价比最高的通用元器件。必要时,须分别对各个没有把握的部分进行搭试、功能检验和性能测试,从模块到系统找到相对优化的方案,画出电路原理图。硬件设计的关键一步就是利用印制板(PCB)计算机辅助设计(CAD)软件对系统的元器件进行布局和布线,接着是印制板加工、装配和硬件调试。

  工作量最大的部分是软件设计。软件设计贯穿整个系统的设计过程,主要包括任务分析、资源分配、模块划分、流程设计和细化、编码调试等。软件设计的工作量主要集中在程序调试,所以软件调试工具就是关键。最常用和最有效的工具是在线仿真器(ICE)。

  2. 第2层次:以EDA工具软件和EOS为开发平台的设计方法。

  随着微电子工艺技术的发展,各种通用的可编程半定制逻辑器件应运而生。在硬件设计时,设计师可以利用这些半定制器件,逐步把原先要通过印制板线路互连的若干标准逻辑器件自制成专用集成电路(ASIC)使用,这样,就把印制板布局和布线的复杂性转换成半定制器件内配置的复杂性。然而,半定制器件的设计并不需要设计人员有半导体工艺和片内集成电路布局和布线的知识和经验。随着半定制器件的规模越来越大,可集成的器件越来越多,使印制板上互连器件的线路、装配和调试费用越来越少,不仅大大减少了印制板的面积和接插件的数量,降低了系统综合成本,增加了可编程应用的灵活性,更重要的是降低了系统功耗,提高了系统工作速度,大大提高了系统的可靠性和安全性。

  这样,硬件设计人员从过去选择和使用标准通用集成电路器件,逐步转向自己设计和制作部分专用的集成电路器件,而这些技术是由各种EDA工具软件提供支持的。

  半定制逻辑器件经历了可编程逻辑阵列PLA、可编程阵列逻辑PAL、通用阵列逻辑GAL、复杂可编程逻辑器件CPLD和现场可编程门阵列FPGA的发展过程。其趋势是集成度和速度不断提高,功能不断增强,结构趋于更合理,使用变得更灵活和方便。

  设计人员可以利用各种EDA工具和标准的CPLD和FPGA等,设计和自制用户专用的大规模集成电路。然后再通过自下而上的设计方法,把用半定制器件设计自制的集成电路、可编程外围器件、所选择的ASIC与嵌入式微处理器或微控制器在印制板上布局、布线构成系统。

  3. 第3层次:以IP内核库为设计基础,用软硬件协同设计技术的设计方法。

  20世纪90年代后,进一步开始了从“集成电路”级设计不断转向“集成系统”级设计。目前已进入单片系统SOC(System o-n a chip)设计阶段,并开始进入实用阶段。这种设计方法不是把系统所需要用到的所有集成电路简单地二次集成到1个芯片上,如果这样实现单片系统,是不可能达到单片系统所要求的高密度、高速度、高性能、小体积、低电压、低功耗等指标的,特别是低功耗要求。单片系统设计要从整个系统性能要求出发,把微处理器、模型算法、芯片结构、外围器件各层次电路直至器件的设计紧密结合起来,并通过建立在全新理念上的系统软件和硬件的协同设计,在单个芯片上完成整个系统的功能。有时也可能把系统做在几个芯片上。因为,实际上并不是所有的系统都能在一个芯片上实现的;还可能因为实现某种单片系统的工艺成本太高,以至于失去商业价值。目前,进入实用的单片系统还属简单的单片系统,如智能IC卡等。但几个著名的半导体厂商正在紧锣密鼓地研制和开发像单片PC这样的复杂单片系统。

  单片系统的设计如果从零开始,这既不现实也无必要。因为除了设计不成熟、未经过时间考验,其系统性能和质量得不到保证外,还会因为设计周期太长而失去商业价值。

  为了加快单片系统设计周期和提高系统的可靠性,目前最有效的一个途径就是通过授权,使用成熟优化的IP内核模块来进行设计集成和二次开发,利用胶粘逻辑技术GLT(Glue Logic Technology),把这些IP内核模块嵌入到SOC中。IP内核模块是单片系统设计的基础,究竟购买哪一级IP内核模块,要根据现有基础、时间、资金和其他条件权衡确定。购买硬IP内核模块风险最小,但付出最大,这是必然的。但总的来说,通过购买IP内核模块不仅可以降低开发风险,还能节省开发费用,因为一般购买IP内核模块的费用要低于自己单独设计和验证的费用。当然,并不是所需要的IP内核模块都可以从市场上买得到。为了垄断市场,有一些公司开发出来的关键IP内核模块(至少暂时)是不愿意授权转让使用的。像这样的IP内核模块就不得不自己组织力量来开发。

  这3个层次各有各的应用范围。从应用开发角度看,在相当长的一段时间内,都是采用前2种方法。第3层次设计方法对一般具体应用人员来说,只能用来设计简单的单片系统。而复杂的单片系统则是某些大的半导体厂商才能设计和实现的,并且用这种方法实现的单片系统,只可能是那些广泛使用、具有一定规模的应用系统才值得投入研制。还有些应用系统,因为技术问题或商业价值问题并不适宜用单片实现。当它们以商品形式推出相应单片系统后,应用人员只要会选用即可。所以,3个层次的设计方法会并存,并不会简单地用后者取代前者。初级应用设计人员会以第1种方法为主;富有经验的设计人员会以第2种方法为主;很专业的设计人员会用第3种方法进行简单单片系统的设计和应用。但所有的设计人员都可以应用半导体大厂商推出的用第3种方法设计的专用单片系统。

  结束语

  目前,在我国3个层次的设计分别呈“面”、“线”、“点”的状态。习惯于第1层次设计方法的电子信息系统设计人员需要逐步向第2层次过渡和发展;第2层次设计方法要由“线”逐步发展成“面”;第3层次设计方法需要国家有关部门根据IT发展战略和规划,组织各方面力量攻关、协调发展。第3层次设计方法要由“点”逐步发展成“线”。

2014-01-07 16:49:43 qq8864 阅读数 2916
似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。

一种简单的信号量实现:

void sem_init( volatile U08 *Sem )
{
	(*Sem)=0;
}

void sem_post( volatile U08 *Sem )
{
	if( 0 == (*Sem) )
		(*Sem)++;
}

U08  sem_wait( volatile U08 *Sem ) 
{
	if(0 == *Sem)
		return 1;

	(*Sem)--;

	return 0;
}

在一个大的while(1)大循环中,利用信号量实现各个函数(任务)的同步。

void Task_SysTime( void )
{   
	static int TaskInitFlg = 0;
	U32	Timer1sCount = 0; 			//时钟计数器个数 
	U32 disstat = 0;
	static int tmrid0 = 0, tmrid1 = 0, tmrid2 = 0, tmrid3 = 0;

	if( 0 == TaskInitFlg )
	{
		OSTimeDlyHMSM( 0, 0, 0, 50 //主要等待任务删除后才创建卡任务
		
		tmrid0 = TimerSet(20);	//定时器0(毫秒定时器)用于键盘、寻卡、定时器中断服务程序,20ms
		tmrid1 = TimerSet(1000);//定时器1(毫秒定时器)用于背显、GPS、定时连接检测、空闲显示
		tmrid2 = TimerSet(500);	//定时器2(毫秒定时器)用于信号显示,500ms
		tmrid3 = TimerSet(500);	//定时器3(毫秒定时器)用于电池显示,500ms

		sem_init( &gSem_EVT_CARDFLG_OK );  					//初始化为没有卡
		
		APP_DisIdle( 2 );								//显示一次时间
		APP_DisVoice();
		
		TaskInitFlg = 1;				//任务初始化完成
	}
	else
        {
		HW_IWDG_ReloadCounter();		 		//清看门狗

		if( 0 == TimerCheck(tmrid0) )
		{
			tmrid0 = TimerSet(20);			//定时器0重新定时, 20ms	

			Timer_ScanKeyboard();		      //20MS键盘扫描 
			Timer_FindCard(); 			//20MS寻卡处理
			TIM20MS_IRQHandler();		//20MS定时器中断服务程序
	       }
      }
}

void Task_Tick( void )
{
	Task_SysError();

	Task_CardProc();

	Task_SysTime();

	Task_MenuProc();

	Task_MtnLink();
	
	Task_CommProc();
}
int main( void )
{
	Sys_Init();	//系统初始化

	while( 1 )                                
	{ 
		Task_Tick();	//任务轮询

		if( 0 == sem_wait( &gSem_EVT_QUIT_APP ) ) 
		   break;     //应用退出
	}
}

以上为借助信号量和定时器实现的一种简单的模拟多任务,其实也算不上是多任务,因为如果一个函数执行时间很长,如何打断它?

以下为借住定时器和任务队列实现的一种模拟多任务:

#include <stdio.h>
#include "timTask.h"
#include "disp.h"

/*=====================================================
=	变量定义
=====================================================*/
//任务队列
typedef struct{
	char 	flagState;	//运行方式  0: 无任务
						//			1: 运行 
	char 	flagRun;	//完成状态	0: 正在计数
						//			1: 计数完成
	char 	flagType;	//处理方式	0: 主任务处理
						//			1: 中断处理
	ulong	cntRun;		//运行计数器
	ulong	numCircle;	//循环计数器
	void (*pTaskFunc)(void);	//任务
}TypeTimTask;

TypeTimTask timTaskTab[TIM_TASK_NUMBER];

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskInit(void)
{
	int i;
	
	for (i=0; i<TIM_TASK_NUMBER; i++)
	{
		timTaskTab[i].pTaskFunc = 0;
		timTaskTab[i].cntRun = 0;
		timTaskTab[i].numCircle = 0;
		timTaskTab[i].flagRun = 0;
		timTaskTab[i].flagState = 0;
	}
	SPT_register_call_back(TimTaskUpdate);
	SPT_set(TIM_TASK_PERIOD *64 / 1000);
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
short TimTaskAdd(ulong fsttim, ulong cirtim, void (*pTaskFunc)(void), uchar type)
{
	int i;
	int pos = -1;
	//查找位置
	for (i=0; i<TIM_TASK_NUMBER; i++)
	{
		if (timTaskTab[i].pTaskFunc == pTaskFunc)
		{
			pos = i;
			break;
		}
		if ((pos == -1) && (timTaskTab[i].flagState == 0))
		{
			pos = i;
		}
	}
	//任务已满
	if (pos == -1)
	{
		return -1;
	}
	//
	timTaskTab[pos].pTaskFunc = pTaskFunc;
	timTaskTab[pos].cntRun = fsttim / TIM_TASK_PERIOD;
	timTaskTab[pos].numCircle = cirtim / TIM_TASK_PERIOD;
	timTaskTab[pos].flagRun = 0;
	timTaskTab[pos].flagType = type;
	timTaskTab[pos].flagState = 1;
	
	return 0;
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskDel(void (*pTaskFunc)(void))
{
	int i;
	
	for (i=0; i<TIM_TASK_NUMBER; i++)
	{
		if (timTaskTab[i].pTaskFunc == pTaskFunc)
		{
			timTaskTab[i].flagState = 0;
			timTaskTab[i].flagRun = 0;
			return;
		}
	}
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskUpdate(void)
{
	int i;
	
	SPT_set(TIM_TASK_PERIOD *64 / 1000);
	
	for (i=0; i<TIM_TASK_NUMBER; i++)
	{
		if (timTaskTab[i].flagState != 0)
		{
			if (timTaskTab[i].cntRun != 0)
			{
				timTaskTab[i].cntRun--;
			}
			else
			{
				//判断处理位置
				if (timTaskTab[i].flagType != 0)
					(*timTaskTab[i].pTaskFunc)();
				else
					timTaskTab[i].flagRun = 1;
				//判断重载
				if (timTaskTab[i].numCircle)
					timTaskTab[i].cntRun = timTaskTab[i].numCircle;
				else
					timTaskTab[i].flagState = 0;
			}
		}
	}
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskProc(void)
{
	int i;
	
	for (i=0; i<TIM_TASK_NUMBER; i++)
	{
		if (timTaskTab[i].flagRun != 0)
		{
			timTaskTab[i].flagRun = 0;
			(*timTaskTab[i].pTaskFunc)();
		}
	}
}
更为巧妙的是,可以借住函数指针实现一种灵活的菜单和按键实时处理结构。类似于windows下win32的消息驱动机制,
通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:

#ifndef __PAGE_H_
#define __PAGE_H_

#include "heads.h"

/*=====================================================
=	
=====================================================*/
typedef struct{
	void (* OnPaint)(void);
	void (* OnKey)(short);
}TypePage;

/*=====================================================
=	
=====================================================*/
void WndPageSet(const TypePage *pg, int type = 0);
TypePage * WndGetPage(void);
void WndPageEsc(void);
void WndOnKey(short key);
void WndOnPaint(void);
void WndMenuInit(const char *pmn, char mline);
void WndMenuSelet(int m);
char WndMenuGetSelet(void);
long WndGetPaseword(int x, int y, char *psw, int len, long qevent);

#include "pageWnd.h"

/*=====================================================
=	
=====================================================*/
char  flagPaint = 0;
void (* pOnPaint)(void) = 0;
void (* pOnKey)(short) = 0;

const char *pMenuStr;
uchar menuSelect = 0;
uchar menuLine = 0;
uchar menuTop;


TypePage *pageCurrent;
TypePage *pageTreeTab[10];
uchar pageIndex = 0;

/*=====================================================
=	
=====================================================*/
void WndDrawMenu(void);

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndPageSet(const TypePage *pg, int type)
{
	if (pg == &pageMain)		//防止出错
	{
		pageIndex = 0;
	}
	else if (type == 0)
	{
		pageTreeTab[pageIndex++] = pageCurrent;
	}
	pageCurrent = (TypePage *)pg;
	pOnPaint = pg->OnPaint;
	pOnKey = pg->OnKey;
	flagPaint = 1;
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
TypePage * WndGetPage(void)
{
	return pageCurrent;
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndPageEsc(void)
{
	TypePage *pg;
	
	if (pageIndex != 0)
	{
		pageIndex--;
		pg = pageTreeTab[pageIndex];
	}
	else
	{
		pg = (TypePage *)&pageMain;
	}
	pageCurrent = pg;
	pOnPaint = pg->OnPaint;
	pOnKey = pg->OnKey;
	flagPaint = 1;
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndOnPaint(void)
{
	if (flagPaint != 0)
	{
		flagPaint = 0;
		(*pOnPaint)();
	}
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndOnKey(short key)
{
	if (pOnKey != 0)
	{
		(*pOnKey)(key);
	}
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndMenuInit(const char *pmn, char mline)
{
	menuSelect = 0;
	pMenuStr = pmn;
	menuLine = mline;
	menuTop = 0;
	WndDrawMenu();
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndMenuSelet(int m)
{
	//光标滑动
	if (m > 0)			//下移
	{
		menuSelect++;
		if (menuSelect == menuLine)
			menuSelect = 0;

		if (menuSelect > menuTop + 4)
		{
			if (menuLine < menuTop + 4)
				menuTop = menuLine - 4;
			else
				menuTop = menuSelect - 4;
		}
	}
	else if (m < 0)		//上移
	{
		if (menuSelect == 0)
			menuSelect = menuLine - 1;
		else
			menuSelect--;
	}
	//图框移动
	if (menuSelect < menuTop)				//上移
	{
		menuTop = menuSelect;
	}
	else if (menuSelect >= menuTop + 4)		//下移
	{
		menuTop = menuSelect - 3;
	}
	
	WndDrawMenu();
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
char WndMenuGetSelet(void)
{
	return menuSelect + 1;
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndDrawMenu(void)
{
	int i;

	char buf[17];
	const char *pmn = pMenuStr + menuTop * 16;

	DispClr();
	for (i=0; i<4; i++)
	{
		if (menuTop + i == menuLine)
			break;
		memcpy(buf, pmn, 16);
		buf[16] = '\0';
		if (menuSelect == menuTop + i)
			DispSetStyle(DISP_POSITION | DISP_REVERSE | DISP_7x9);
		else
			DispSetStyle(DISP_POSITION | DISP_NORMAL | DISP_7x9);
		DispString(0, i * 2, buf);
		pmn += 16;
	}
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
long WndGetPaseword(int x, int y, char *psw, int len, long qevent)
{
	int pin = 0;
	long keyevt;
	char key;
	char buf[20];
	
	memset(buf, '_', len);
	buf[len] = '\0';
PSW_INPUT_LOOP:
	DispString(x, y, buf);
	keyevt = delay_and_wait_key(0, EXIT_KEY_ALL, 0);
	if (keyevt == qevent)
	{
		psw[0] = '\0';
		return keyevt;
	}
	switch (keyevt)
	{
		case EXIT_KEY_0:
			key = '0';
			break;
		case EXIT_KEY_1:
			key = '1';
			break;
		case EXIT_KEY_2:
			key = '2';
			break;
		case EXIT_KEY_3:
			key = '3';
			break;
		case EXIT_KEY_4:
			key = '4';
			break;
		case EXIT_KEY_5:
			key = '5';
			break;
		case EXIT_KEY_6:
			key = '6';
			break;
		case EXIT_KEY_7:
			key = '7';
			break;
		case EXIT_KEY_8:
			key = '8';
			break;
		case EXIT_KEY_9:
			key = '9';
			break;
		case EXIT_KEY_COMM:
			if (pin != 0)
			{
				buf[--pin] = '_';
			}
			goto PSW_INPUT_LOOP;
			break;
		case EXIT_KEY_ENTER:
			psw[pin] = 0;
			return 0;
		default:
			goto PSW_INPUT_LOOP;
	}
	if (pin != len)
	{
		psw[pin] = key;
		buf[pin] = '*';
		pin++;
	}
	goto PSW_INPUT_LOOP;
}

在软件设计时,如果添加界面和对应的按键处理,很灵活,只需要新添加一个文件就可以了,文件的内容,只需要实现OnPain和对应的OnKey

#include "PageMenu.h"


/*=====================================================
=	
=====================================================*/
const char mainMenuTab[] = /*
1234567890123456*/"\
1. 现场采集     \
2. 数据上传     \
3. 存储状态查询 \
4. 时间设置     \
5. 对比度设置   \
6. 恢复出厂设置 \
7. 关于         ";

/*=====================================================
=	
=====================================================*/
void PageMenuOnPain(void);
void WndMenuOnKey(short key);

const TypePage pageMenu = {PageMenuOnPain, WndMenuOnKey};

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PageMenuOnPain(void)
{
	WndMenuInit(mainMenuTab, 7);
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndMenuOnKey(short key)
{
	int res;
	
	switch (key)
	{
		case KEY_F1:
		case KEY_ENTER:
			res = WndMenuGetSelet();
			switch (res)
			{
				case 1:
					WndPageSet(&pageSimp);
					break;
				case 2:
					WndPageSet(&pagePclink);
					break;
				case 3:
					WndPageSet(&pageInquire);
					break;
				case 4:
					WndPageSet(&pageRtc);
					break;
				case 5:
					WndPageSet(&pageGray);
					break;
				case 6:
					SPageInit();
					WndPageSet(&pageMenu, 1);
					break;
				case 7:
					WndPageSet(&pageAbout);
					break;
			}
			break;
		case KEY_F2:
		case KEY_F3:
			WndPageSet(&pageMain);
			break;
		case KEY_1:
			WndPageSet(&pageSimp);
			break;
		case KEY_2:
			WndPageSet(&pagePclink);
			break;
		case KEY_3:
			WndPageSet(&pageInquire);
			break;
		case KEY_4:
			WndPageSet(&pageRtc);
			break;
		case KEY_5:
			WndPageSet(&pageGray);
			break;
		case KEY_6:
			SPageInit();
			WndPageSet(&pageMenu, 1);
			break;
		case KEY_7:
			WndPageSet(&pageAbout);
			break;
		case KEY_UP:
			WndMenuSelet(-1);
			break;
		case KEY_DOWN:
			WndMenuSelet(1);
			break;
		case KEY_POWER:
			WndPageSet(&pagePower);
			break;
	}
}

pageMain,pageAbout,pageRtc,pagePclink等文件,他们的结构很类似。都是实现了OnPaint和OnKey函数。
如:pagePclink.c文件内容:
实现了PagePclinkOnPaint和PagePclinOnKey函数.

CommPclink函数是自己想要实现的功能,可以自己定义。

#include "pagePclink.h"

/*=====================================================
=	
=====================================================*/
void PagePclinkOnPaint(void);
void PagePclinOnKey(short key);

const TypePage pagePclink = {PagePclinkOnPaint, PagePclinOnKey};

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePclinkOnPaint(void)
{
	DispClr();
	DispSetStyle(DISP_CENTER | DISP_REVERSE | DISP_7x9);
	DispString(0, 0, "    数据上传    ");
	DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
	DispString(0, 6, "[连接]    [返回]");
}

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePclinOnKey(short key)
{
	switch (key)
	{
		case KEY_F1:
			CommPclink();
			break;
		case KEY_F3:
			WndPageEsc();
			break;
	}
}

#ifndef __PAGE_POWER_H_
#define __PAGE_POWER_H_

#include "pageWnd.h"

/*=====================================================
=	
=====================================================*/
extern const TypePage pagePower;

#endif

#include "PagePower.h"
#include "disp.h"

/*=====================================================
=	
=====================================================*/
void PagePowerOnPaint(void);
void PagePowerOnKey(short key);

const TypePage pagePower = {PagePowerOnPaint, PagePowerOnKey};

/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePowerOnPaint(void)
{
	DispClr();
	DispSetStyle(DISP_CENTER | DISP_REVERSE | DISP_7x9);
	DispString(0, 0, "    电源管理    ");
	DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
	DispString(0, 2, "  [Enter] 关机  ");
	DispString(0, 4, "  [F3   ] 返回  ");
}


/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePowerOnKey(short key)
{
	switch (key)
	{
		case KEY_ENTER:
		case KEY_POWER:
			Halt_EH0218(4);
			SysInit();
			break;
		case KEY_F3:
			WndPageEsc();
			break;
	}
}

这样的一种结构,很灵活,在主函数中只需要这样调用:

int main(void)
{
	short key;
	typ_msg_word smw;
	
	SysInit();

	for ( ; ; )
	{
		/*
		  界面刷新
		*/
		WndOnPaint();

		/*
		  消息处理
		*/
		smw.s_word = sys_msg(SM_STAY_AWAKE);	//用SM_GOTO_SLEEP串口就不能用
		//按键处理
		if (smw.bits.key_available)	
		{
			LcdOffDelay(LCD_OFF_DELAY);
			
			key = KEY_read();
			if (key != -1)
			{
				WndOnKey(key);
			}
		}
		//插入充电电源
		if (smw.bits.charger_on)
		{
			LcdOffDelay(LCD_OFF_DELAY);
		}
		//断开充电电源
		if (smw.bits.charger_off)
		{
			LcdOffDelay(LCD_OFF_DELAY);
			RefreshBattery();
		}
		//串口
		if (smw.bits.comm_data)
		{
			CommReceive();
		}
		//实时任务
		if (smw.bits.time_out)
		{
			TimTaskProc();
		}
	}
}


2020-01-17 14:10:03 qq_45763093 阅读数 3

1、硬件层,是整个嵌入式系统的根本,如果现在单片机及接口这块很熟悉,并且能用C和汇编语言来编程的话,从嵌入式系统的硬件层走起来相对容易,硬件层也是驱动层的基础,一个优秀的驱动工程师是要能够看懂硬件的电路图和自行完成CPLD的逻辑设计的,同时还要对操作系统内核及其调度性相当的熟悉的。但硬件平台是基础,增值还要靠软件。

硬件层比较适合于,电子、通信、自动化、机电一体、信息工程类专业的人来搞,需要掌握的专业基础知识有,单片机原理及接口技术、微机原理及接口技术、C语言。

2、驱动层,这部分比较难,驱动工程师不仅要能看懂电路图还要能对操作系统内核十分的精通,以便其所写的驱动程序在系统调用时,不会独占操作系统时间片,而导至其它任务不能动行,不懂操作系统内核架构和实时调度性,没有良好的驱动编写风格,按大多数书上所说添加的驱动的方式,很多人都能做到,但可能连个初级的驱动工程师的水平都达不到,这样所写的驱动在应用调用时就如同windows下我们打开一个程序运行后,再打开一个程序时,要不就是中断以前的程序,要不就是等上一会才能运行后来打开的程序。想做个好的驱动人员没有三、四年功底,操作系统内核不研究上几编,不是太容易成功的,但其工资在嵌入式系统四层中可是最高的。  在嵌入式行业,做驱动层当然是最有前途的,但是难度也是最大的,其专业知识要求也是最广的。

驱动层比较适合于电子、通信、自动化、机电一体、信息工程类专业尤其是计算机偏体系结构类专业的人来搞,除硬件层所具备的基础学科外,还要对数据结构与算法、操作系统原理、编译原理都要十分精通了解。

3、操作系统层,对于操作系统层目前可能只能说是简单的移植,而很少有人来自已写操作系统,或者写出缺胳膊少腿的操作系统来,这部分工作大都由驱动工程师来完成。操作系统是负责系统任务的调试、磁盘和文件的管理,而嵌入式系统的实时性十分重要。据说,XP操作系统是微软投入300人用两年时间才搞定的,总时工时是600人年,中科院软件所自己的女娲Hopen操作系统估计也得花遇几百人年才能搞定。因此这部分工作相对来讲没有太大意义。

4、应用层,相对来讲较为容易的,如果会在windows下如何进行编程接口函数调用,到操作系统下只是编译和开发环境有相应的变化而已。如果涉及Java方面的编程也是如此的。嵌入式系统中涉及算法的由专业算法的人来处理的,不必归结到嵌入式系统范畴内。但如果涉及嵌入式系统下面嵌入式数据库、基于嵌入式系统的网络编程和基于某此应用层面的协议应用开发(比如基于SIP、H.323、Astrisk)方面又较为复杂,并且有难度了。所以对应用层主要是,编程语言和开发工具学习,以及良好的编程习惯和软件工程的,如果要增加自己在某个领域的竞争力,不妨学习一下TCP/IP中某层的协议。

2012-04-05 11:45:53 zy_qianrushi 阅读数 302

       这是过去直至现在我国单片机应用系统设计人员一直沿用的方法,其步骤是先抽象后具体。下面由卓跃教育一一为您介绍。
  
  第1层次:以PCBCAD软件和ICE为主要工具的设计方法。
  
  抽象设计主要是根据嵌入式应用系统要实现的功能要求,对系统功能细化,分成若干功能模块,画出系统功能框图,再对功能模块进行硬

件和软件功能实现的分配。
  
  具体设计包括硬件设计和软件设计。硬件设计主要是根据性能参数要求对各功能模块所需要使用的元器件进行选择和组合,其选择的基本

原则就是市场上可以购买到的性价比最高的通用元器件。必要时,须分别对各个没有把握的部分进行搭试、功能检验和性能测试,从模块到系

统找到相对优化的方案,画出电路原理图。硬件设计的关键一步就是利用印制板(PCB)计算机辅助设计(CAD)软件对系统的元器件进行布局和布

线,接着是印制板加工、装配和硬件调试。
  
  工作量最大的部分是软件设计。软件设计贯穿整个系统的设计过程,主要包括任务分析、资源分配、模块划分、流程设计和细化、编码调

试等。软件设计的工作量主要集中在程序调试,所以软件调试工具就是关键。最常用和最有效的工具是在线仿真器(ICE)。
  
  第2层次:以EDA工具软件和EOS为开发平台的设计方法。
  
  随着微电子工艺技术的发展,各种通用的可编程半定制逻辑器件应运而生。在硬件设计时,设计师可以利用这些半定制器件,逐步把原先

要通过印制板线路互连的若干标准逻辑器件自制成专用集成电路(ASIC)使用,这样,就把印制板布局和布线的复杂性转换成半定制器件内配的

复杂性。然而,半定制器件的设计并不需要设计人员有半导体工艺和片内集成电路布局和布线的知识和经验。随着半定制器件的规模越来越大

,可集成的器件越来越多,使印制板上互连器件的线路、装配和调试费用越来越少,不仅大大减少了印制板的面积和接插件的数量,降低了系

统综合成本,增加了可编程应用的灵活性,更重要的是降低了系统功耗,提高了系统工作速度,大大提高了系统的可靠性和安全性。
  
    半定制逻辑器件经历了可编程逻辑阵列PLA、可编程阵列逻辑PAL、通用阵列逻辑GAL、复杂可编程逻辑器件CPLD和现场可编程门阵列FPGA的

发展过程。其趋势是集成度和速度不断提高,功能不断增强,结构趋于更合理,使用变得更灵活和方便。
  
  设计人员可以利用各种EDA工具和标准的CPLD和FPGA等,设计和自制用户专用的大规模集成电路。然后再通过自下而上的设计方法,把用半

定制器件设计自制的集成电路、可编程外围器件、所选择的ASIC与嵌入式微处理器或微控制器在印制板上布局、布线构成系统。
 

WinCE系统架构

阅读数 727

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