📄 mrv.c
字号:
/**** 这里的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 + -