⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mrv.c

📁 一款C语言编写的小家电程序
💻 C
📖 第 1 页 / 共 5 页
字号:


/**** 这里的RAM是从0x0100开始在0xffff前结束的 ****/
 /*** 特别注意一下写法是__near ***/
//unsigned char	__near	wqtemp[10];		// 数组



/************************************************/
/*	ROM表格的定义,具体表格在后面定义	*/
/************************************************/

static	const	unsigned int	TBL_DChar[];	// 本系统所用的数字及字母的显示表格
static	const	unsigned char	TBL_Sentense[];	// 表格是每一句话所用单词所组成,每一句话都是单词所对应的位置值组成
static	const	unsigned char	TBL_Word[];	// 单词表格,每一行由组成单词的每个字母的对应的值所组成
static	const	unsigned char	TBL_VPC[];	// 微波火力表格,由微波开/关时间组成,其计算单位为62.5毫秒
static	const	unsigned char	TBL_KyConv[];	// 按数字键时,将读键值转为数字的表格
static	const	unsigned char	TBL_MenuInf[];	// 菜单操作时,菜单的相关资料
static	const	unsigned char	TBL_MenuMaxTime[]; // 菜单操作时,每种菜单最大的对应的时间
static	const	unsigned char	TBL_CompuLB[];	// 这里是英磅盎司的单位对应的时间
static	const	unsigned char	TBL_CompuKG[];	// 这里是千克的单位对应的时间
static	const	unsigned int	TBL_Scale[];	// 本表格用于定义各种音阶的频率
static	const	unsigned char	TBL_Music1[];	// 本系统所用的第一首歌的谱
static	const	unsigned char	TBL_RotEn[];	// 允许旋扭有功能的模式
static	const	unsigned char	TBL_ADT[];	// AD读值对应的温度



/************************************************/
/*	函数定义				*/
/************************************************/

/**** startup是在外部定义的,本来应该注明是extern,但是由于在外面那个程序里 ****/
 /*** 我们用的是_startup,即加了一_,所以在这里可以这么定义 ***/
 /*** 所以如果在外面处理的话,可能和_有关 ***/
 /*** 这些程序是跟系统有关的 ***/

 /*** 本来一定要加上startup用汇编所写的东西,但是我研究之后发现以下的理由成为我不加的原因 ***/
 /*** 第一,我想用标准的C程序 ***/
 /*** 第二,虽然程序中断后产生一个PUSH,但是在跟著的程式里我清除了RAM,而主程序是不停的死循环的 ***/
 /*** 因此,在主程序里是不会退出的,所以没有必要考虑这个事情 ***/
 /*** 也因此,我的想法应该是可行的 ***/
 /*** 问题在于,如果是OTP,会否有第一次的影响,因我发现,第一次有可能有问题 ***/
 /*** 汇编里的那个,下面不需作任何更改,用于补漏,因为870系统复位后堆栈是不确定的,专用汇编作 ***/
 /*** 这几个程序都是系统程式,最基本的几个处理程序循环 ***/
 /*** 除非是非常重要并且对时间的准确性要求特别高的情况下,方才放在中断程序里处理,否则一律放在主程序里面 ***/
 /*** 通常来说,只有发码,LED显示驱动,音乐,接收信号等等才会对时间要求特别严格 ***/
void	__near		startup(void);		// 程式的主流程,主要是初始化及主循环,结构尽可能简单
void	__interrupt	INT_Dummy(void);	// 所用未用的中断处理
void	__interrupt	INT_INTTBT(void);	// 基本时钟中断


/**** 以下是子程序,跟实际要求有关的 ****/
void	__near	init(void);			// 初始化,对各种特别寄存器,管脚,内存,时钟,看门狗的初始化
void	__near	iproj(void);			// 初始化本项目一些特定的处理,给一些特定的资料赋初值
void	__near	gettimeunit(void);		// 程序只定义尽可能少的时钟的基本单位,其它的时钟单位由该单位计算得出
void	__near	testpin(void);			// 此程序用于编程过程中的测试

void	__near	kyscan(void);			// 键盘的驱动程式,每15.625毫秒做一次
void	__near	kydeal(void);			// 键盘的具体处理过程
void	__near	kd_number(void);		// 数字按键的处理程序模块
void	__near	kd_lock(void);			// 锁定按键的处理程序模块
void	__near	kycomf(void);			// 按键后共同的一些处理,比如一定点亮背光灯,初始化一分钟的资料等等
void	__near	kyhold(void);			// 按住键不放的处理
void	__near	kd_stopclear(void);		// 对STOP/CLEAR键的处理程序,回到RTC模式
void	__near	kd_lighthilo(void);		// 对LIGHT键的处理程序,关灯,高亮度,低亮度,又关灯,如此重复
void	__near	kd_fanhilo(void);		// 对FAN键的处理程序,无风,强风,弱风,又无风,如此重复
void	__near	kd_minuteplus(void);		// 对MINUTEPLUS键的处理,此键进入快速烹调模式
void	__near	kd_turntableonoff(void);	// 按此键在大多数模式下,开/关转盘
void	__near	kd_powerlevel(void);		// 对POWER/LEVEL键的处理程式
void	__near	kd_menu(void);			// 各种菜单的处理
void	__near	kd_compucook(void);		// COMPU模式共六种模式
void	__near	kd_compudefrost(void);		// COMPU解冻共六种模式
void	__near	kd_starttouchon(void);		// 开始及按住不放计时按键
void	__near	kd_customhelp(void);		// 帮助按键
void	__near	kd_kitchentimerclock(void);	// 定时器及时钟设定按键

void	__near	rtc(void);			// 实时时钟计时
void	__near	doorcheck(void);		// 检查门的状态
void	__near	modeconv(void);			// 各种模式随时间的自动转换

void	__near	beepdrv(void);			// BEEP声的驱动程式,每15.625毫秒做一次
void	__near	beep1(void);			// 短BEP一声
void	__near	beep4l(void);			// 长BEP四声

void	__near	scaledrv(void);			// 音阶的驱动程序,根据程序具体情况,将管脚不停的高低发声
void	__near	playmusic(void);		// 音乐的驱动程序,根据数据表格发音乐

void	__near	n1add(void);			// 一位十进制的加法运算
void	__near	n1sub(void);			// 一位十进制的减法运算
void	__near	n2shift(void);			// 两8位即4个4位左移位的操作

void	__near	updd(void);			// 更新显示内容,每62.5毫秒做一次,根据系统模式,决定显示内容
void	__near	sentensedisp(void);		// 句子显示的驱动程序,依次显示句子的单词,给人是闪烁显示的感觉
void	__near	valuedisp(void);		// 根据选定的内存单元,将其值显示出来
void	__near	svcdisp(void);			// 一些特别的显示联连句子显示的方法
void	__near	updicon(void);			// 图标的显示
void	__near	dispbasedrv(void);		// 显示的最底层驱动程序,将准备好的内容查表显示出来
void	__near	sdispinit(void);		// 句子显示的初始化准备工作
void	__near	scomdt(void);			// 向HT16215发射显示的命令及数据
void	__near	scom(void);			// 向HT16215发射显示的命令
void	__near	sdt(void);			// 向HT16215发射显示的数据
void	__near	dht(void);			// HT16215显示底层驱动程序

void	__near	vpc(void);			// 微波驱动程序
void	__near	lamp(void);			// Lamp及转盘驱动程式
void	__near	cooking(void);			// 烹调进入倒计时过程
void	__near	cookinfclr(void);		// 烹调资料清除程式
void	__near	computime(void);		// COMPU COOK及COMPU DEF烹调时间的计算

void	__near	stability(void);		// 等待系统稳定
void	__near	rotdrv(void);			// 旋扭驱动程序
void	__near	addrv(void);			// A/D驱动程序
void	__near	adcal(void);			// A/D读值运算



/************************************************/
/*	主流程及中断程序			*/
/************************************************/

/**** 主流程 ****/
 /*** 这里包括以下几个方面 ***/
 /*** 程式自身的初始化,包括RAM,特别寄存器,管脚,特别内存单元 ***/
 /*** 程序自身的循环,包括立即,各种时间单位的 ***/
void	__near	startup(void)
{
 /*** 复位时最好首先关掉中断 ***/
 /*** 通常来说,复位时是中断是关掉的,但是在现实中有可能是带电复位,那么这种情况下就比较危险 ***/
 /*** 所以复位时关中断是有必要的,并不多余 ***/
	__asm("DI");

 /*** 通常来说,不需要做一些特别的处理 ***/
 /*** 但有两种情况:
 /*** 第一:有一些管脚的要求特别的高,而由于在程序设计时使得开机会启动一个很短的时间,这时需要复处理这些管脚 ***/
 /*** 第二:可以通过程序方面的处理,让死机时假装没有死机,从而使得死机的现象有所减少,这时需要对RAM做特别处理 ***/

 /*** 这里是内存的初始化 ***/
 /*** 由于这一段程序特别不太好处理,所以在这里用汇编处理 ***/
 /*** 我看过LST文件,如果强行用C,第一特别麻烦,第二未必能很顺利的处理而且相当长 ***/
	__HL	= CRAMMIN;
	__BC	= CRAMMAX - CRAMMIN;
	__asm("INIT_RAM:	LD	(HL),0x00	");
	__asm("			INC	HL		");
	__asm("			DEC	BC		");
	__asm("			J	F,INIT_RAM	");

 /*** 这时初始化已完成,因此可以在这里指定堆栈的初始值 ***/
 /*** 对于870C来说,不必要减8个单元用于保证安全,而对于870来说,减8个单元保证安全最好了 ***/
 /*** 没有理由,纯是经验之谈 ***/
	__SP	= CRAMMAX;

 /*** 对系统进行初始化 ***/
 /*** 具体包括:键盘输出口,读系统选项,中断,A/D,时钟,显示及看门狗 ***/
	init();

 /*** 系统初始化完成以后才打开中断 ***/
 /*** 一定要在退出系统初始化的模块,回到主程序里时才这样做,这样可防止在子程序里中断 ***/
	__asm("EI");

 /*** 初始化本项目特别的一些定义区 ***/
 /*** 因为有时候可能会用到与时间相关的资料及处理,所以要在开中断之后 ***/
	iproj();

 /*** 这里进入主流程的循环等待过程 ***/
 /*** 本程序中的主流程只需作看门狗的清除和循环就可以了 ***/
	for(;;)
	{
		WDTCR2	= WDOGCLR;			// 清除看门狗计数器
		kydeal();				// 键盘处理程序

		if (F_BTTUnit == 0x01)			// 此处是程序最基本时间单位的处理程式
		{
			F_BTTUnit = 0x00;		// 清掉该时间单位的值,并处理该时间单位的事件,要尽可能考虑时间长度
			gettimeunit();			// 以最基本时间单位为基准得到其它各种时间单位,通常15~62毫秒左右
			kyscan();			// 最底程的键盘扫描程序,通过输出口及读入得到键盘按键值
			beepdrv();			// 最底程的蜂鸣器驱动程序
			doorcheck();			// 检查门的状态
			playmusic();			// 根据电子音乐的指针指出新的音谱设定新的计时/计数中断1的中断时间
		}

		if (F_62ms == 0x01)
		{
			F_62ms = 0x00;			// 清掉62.5毫秒标志,处理62.5毫秒的工作
			kyhold();			// 按住键盘的处理
			updd();				// 更新显示内容及驱动显示
			dispbasedrv();			// 显示的最底程软件,更新显示驱动区的资料并显示出来
			lamp();				// Lamp及转盘驱动程式
			modeconv();			// 模式的自动转换
		}

		if (F_1s == 0x01)
		{
			F_1s = 0x00;			// 清掉1秒标志,处理1秒的工作
			rtc();				// 实时时钟工作
			cooking();			// 烹调倒计时进行过程
		}
	}
}


/**** 计时/计数中断1 ****/
 /*** 本中断的时间不确定,主要用来是发声的,其原理如下: ***/
 /*** P1.3本来是可以用系统自带的分频来发声的,但是由于这是电子音乐,因此不能这样简单的做 ***/
 /*** 因此,这里虽然只是发声,但需要不停的变换频率 ***/
 /*** 这里只是根据所设定好的时间及频率不停的转换高低输出 ***/
void	__interrupt	INT_INTTC1(void) { scaledrv(); }


/**** 基本时钟中断 ****/
 /*** 该中断为非常准确的中断,可以用该中断计算出一个用于最基本的时间单位的标志 ***/
 /*** 根据此标志可通过一定的方法得到其它的时间单位 ***/
void	__interrupt	INT_INTTBT(void) { F_BTTUnit = 0x01; }


/**** 未用之中断 ****/
 /*** 在这里,有一个问题,即有两个不可取消的中断一是INT1,一是WDT ***/
 /*** 这两个中断都是一定会有的,所以当不用的时候,也要跳到此处 ***/
void	__interrupt	INT_Dummy(void) {}



/************************************************/
/*	子程序					*/
/************************************************/

/**** 对系统进行初始化 ****/
 /*** 具体包括:键盘输出口,读系统选项,晶振时钟的设置,A/D,显示,中断及看门狗 ***/
void	__near	init(void)
{
 	SYSCR2	= SINGLECLOCK;			// 选择高频晶振控制
	WDTCR1	= WDOGEN;			// 使能看门狗
	WDTCR2	= WDOGCLR;			// 清除看门狗计数器
}


/**** 初始化特定的一些资料 ****/
 /*** 根据具体的版本号对某些特定的版本作一些特定的处理 ***/
void	__near	iproj(void)
{
}


/**** 测试子程序 ****/
 /*** 该程序专用于测试,现象是将一个管脚不停的反向 ***/
 /*** 在这里,这个程序已无用,因为,该管脚已受别的程序控制 ***/
 /*** 只是该程序写法上是这样写,由于管脚不一定可读,所以用一个RAM表示管脚状态 ***/
 /*** 其实用来做测试的方法很多:可以通过管脚变化,可以通过RAM变化,可以设断点... ***/
void	__near	testpin(void)
{
	if (F_WQTest == 0x01) { F_WQTest = 0x00; PIN_Test = 0x00; }
	else { F_WQTest = 0x01; PIN_Test = 0x01; }
}


/**** 根据基本时钟中断15.625毫秒得到其它的时钟单位 ****/
 /*** 先让计数单元不停的增加,跟著除以2余数为0,则表示未一位为0,也即为31毫秒 ***/
 /*** 同样道理,除四后,余数为0,则表示未两位为00,也即增加了4个,即62毫秒 ***/
 /*** 除四零后,余数为0,则表示未六位为000000,也即增加了64次,即1秒 ***/
void	__near	gettimeunit(void)
{
	R_BTSlot++;
	if (R_BTSlot%0x04 == 0x00) { F_62ms = 0x01; }
	if (R_BTSlot%0x40 == 0x00) { F_1s = 0x01; }
}


/**** 键盘的驱动程式 ****/
 /*** 首先先要初始化各个键盘输出管脚 ***/
 /*** 本来如果其它未用的管脚假如都用作输入的话,那么就可以整个管脚一齐置高 ***/
 /*** 但是在现实中往往可能不是这样,所以还是位置比较妥当 ***/
void	__near	kyscan(void)
{
 // 首先初始化管脚及扫描指针的值,最好是用位置,因为别的管脚的用途不知道
 // 扫描指针及两个可能用到的存储单元
	PIN_KBOP0 = 0x00; PIN_KBOP1 = 0x00;		// 将输出管脚全部初始化
	R_Pointer = 0x00;				// 读键的指针也全部初始化为0x00
	R_Temp[0] = 0x00; R_Temp[1] = 0x00;		// 头一个缓冲区用来读按键输入口,而另一个用来得到确定的按键值

 // 扫描指针小于0x02并且R_KSTemp即所读到的按键值为0,也即没有按键,才继续进行读键扫描
 // 根据这个项目的特点,只需要在每一行的上面分别加上0x11,0x22,0x33,0x44,0x55,0x66等等
 // 这样就可以区别开所有的按键值,因此根据输出口及输入口的关系,每次加0x11
 // 当扫描了所有输出口后,指针为0x02,这时不再扫描
 // 而当有按键发生的时候,这时R_KSTemp也将不再为0x00,所以这时也不会再扫描
 // 而每次输出口变换的时候,R_KSTemp2已经加了0x11,所在可区别按键是哪个输出口对应的
	while((R_Pointer < 0x02) && (R_Temp[0] == 0x00))		// 没扫描完,并且没有读到按键继续做
	{
		if (R_Pointer == 0x00)	PIN_KBOP0 = 0x01;
		else if (R_Pointer == 0x01)	PIN_KBOP1 = 0x01;

		R_Pointer++;
		R_Temp[0] = PINKYIN; R_Temp[0] = R_Temp[0] & 0xFF;	// 去掉不相关的管脚
		R_Temp[1] = R_Temp[1] + 0x11;				// 区别是哪一个输出口
	}
	if (R_Temp[0] != 0x00)	R_Temp[0] = R_Temp[0] + R_Temp[1];	// 如果没有按键,不需要加,如果有按键,才需要区别

 // 这里已经确定了按键,首先要去抖动,才确认
 // 如果是没有按键,则将标志有键动作的标志清掉
 // 这主要是为了用于按键处理时只处理一次,即按住键不放也只当一次按键处理
	if (R_Temp[0] != R_KeyBuf) { R_KeyBuf = R_Temp[0]; }
	else
	{
		R_CurKey = R_Temp[0];
		if (R_Temp[0] == 0x00) F_KeyAction = 0x00;
	}
}


/**** 键盘的具体处理过程 ****/
 /*** 当没有按键及按住不放时都不处理,按键只处理一次 ***/
 /*** F_KeyAction这个标志,当有按键的时候是为1的,没有按键的时候是为0的,所以为0时才处理 ***/
void	__near	kydeal(void)
{
 // F_KeyAction这个标志,当有按键的时候为1,没有按键为0的
 // 当每次松开按键时,将此标志清掉,因此,进入到此程式中,当有键时,它第一次为0,因此要处理
 // 可是当处理完后,即将此标志置1,因此下次再来的时候,此标志为1,不用再处理

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -