现在将一个实际编制的、带有调整时间功能的算盘码新型时钟程序发布。硬件电路由于只有一个三基色LED和一个按键开关,就不贴出了。所选MCU主要是利用其内置的RTCC。
单个按键开关有短按和长按两种用法。在正常报时状态,短按无作用,长按可以进入调时状态。进入调时状态后,按照十小时、小时、十分、分的顺序依次调整。每次短按键循环进行增量步进,长按键进入下一档位,4种都调完最后一次长按键就回到正常报时状态。在调整状态,调整“小时”和“分钟”时用白色和蓝色分别显示,白色表示正在调整的档位,蓝色则表示不调整的档位。例如,在调整到第三档的时候,第一个拍发的数码是白色,表示“十分”,第二个拍发的数码是蓝色,表示“分”,在这个档位短按键只调整白色的“十分”,不调整蓝色的“分”。
/*-------------------------------------------------------------------
编译器:MCC18 V3.40
日期:2011 0924
版本:V1.1
功能:用过去在Pic16论坛上发布过的算盘码编码格式,在pic18f26j13的RTCC上实现可以
用两位码和光色来表示小时与分钟的新型时钟。
前一位码的光色表示“十小时”,码型表示“小时”。后一位码的光色表示“十分钟”,码型表示“分钟”。
光色表示的数值依照“蓝,绿,黄,红,青,紫”依次表示“0,1,2,3,4,5”。
码型以长短发光表示,以“划”表示发光时间略长,“点”表示发光时间很短。以下从0--9的码型分别为
划划,点,点点,点点点,点点点点,划,划点,划点点,划点点点,划点点点点。
用较短的停顿区分前后码,用较长的停顿表示分组。
例如,21点整,表示为“黄点,蓝划划”
修改时钟,要长按唯一的按键,直到发出持续的白光(白里泛紫),松键就用白光点划表示等待修改的位,用蓝光点划表示不修改的位。
修改状态连续发出的两个码型,前面一组表示十小时和小时,后面一组表示十分钟和分钟。每次修改时,先修改十小时位,再修改小时位。
此时显示的两个码前面是十小时,后面是小时。前码白光后码蓝光时修改十小时,通过短按键循环改变该位的值,修改完毕长按键就转入后位修改,
这时是前码蓝光,后码白光,同样是循环改变,完成这组后长按就进入十分钟和分钟修改。后一组的修改过程和结束方式同前。
长按键要看到一组两个码完整显示再松手,才能保证进入下一步。
修改完成后会自动保存到RTCC,继续正常发光报时任务。
硬件电路非常简单,除去主振荡器12m和实时钟晶体32768Hz外,只有程序最前部键定义连接的一个按键和一个三基色led。其它均按MCU手册要求配电容电阻即可。
特别声明,本算盘码的编码方式,修改时钟方式和整个程序属于本人原创,可基于个人研究目的进行引用和复制,但不得作为商业用途使用。
-------------------------------------------------------------------*/
#include <P18f26j13.h>
#define Key PORTCbits.RC2
#define LEDR TRISBbits.TRISB2 //红led
#define LEDG TRISBbits.TRISB0 //绿led
#define LEDB TRISBbits.TRISB3 //蓝led
void main (void);
void InterruptHandlerHigh (void);
unsigned char *ppptr;
static const unsigned int morse_code[12]={0xee00,0x8000,0xa000,0xa800,0xaa00,0xe000,0xe800,0xea00,
0xea80,0xeaa0,0x0000,0x0000}; //算盘码码型
static const unsigned char code_lenth[12]={0x0c,0x06,0x08,0x0a,0x0c,0x08,0x0a,0x0c,0x0f,0x11,0x0a,0x0a};
//算盘码码长
unsigned int *morse_ptr;
unsigned char *code_lenth_ptr;
static unsigned char morse_lenth;
static unsigned int timing_code[4]={0x0000,0xaa00,0x0000,0x0000}; //轮流拍发的码型存放位置
static unsigned char timing_code_lenth[4]={0x05,0x05,0x00,0x05}; //轮流拍发的码长存放位置
unsigned char tens_or_ones;
unsigned char colour,minus;
unsigned char key_pules,key_count;
static unsigned long key_status=0xffffffff; //存储按键历史的变量,每次中断存一位,为0表示长按键
unsigned char key_down,key_up,last_key; //表征按键按下、抬起的变量
unsigned char t=0;
unsigned char minut_colour,minut;
unsigned char hour,hour_colour;
unsigned char in_hour=1; //区别小时和分钟的变量
unsigned int temp;
unsigned int temperature; //测试的变量名,没有改正
unsigned char Rtimehi[4]={0x0,0x09,0x1,0x54},Rtimelo[4]={0x11,0x11,0x15,0x30}; //保存RTCC日期时间的两组变量
//***************主函数*****************
void main()
{
unsigned char i,j;
//**********RC2作为按键使用初始化**********************
ANCON1bits.PCFG11=1; //RC2作为数字IO,不给ADC
TRISCbits.TRISC2=1; //输入,接Key
//*************************RTCC初始化*******************
T1GCON=0;
T1CON=0X8B; //允许tmr1的振荡器
RTCCFG=0X07;
RTCCAL=0X0; //校准值
PADCFG1=0X02; //输出秒时钟
_asm movlw 0x55 _endasm
_asm movwf EECON2,0 _endasm
_asm movlw 0xAA _endasm
_asm movwf EECON2,0 _endasm
_asm bsf RTCCFG,5,1 _endasm //开启写入
RTCCFGbits.RTCEN=1;
RTCCFG|=0x03; //置指针为0b11
while (RTCCFGbits.RTCSYNC); //等待不忙
for (i=0;i<4;i++) //读
{
Rtimelo[i]=RTCVALL;
Rtimehi[i]=RTCVALH;
}
//****************TIMERS*******************
T0CON=0X84; //定时器, 分频
INTCONbits.T0IE=1;
T5CON=0X3F; //1/4Fosc,1:8 prescale,T1OSC, 16 bits WR,enabled
T5GCON=0; //no gate
PIE5bits.TMR5IE=1;
//*****************中断开启***************
INTCONbits.PEIE=1;
INTCONbits.GIE=1;
//****************按键相关设置*************
morse_ptr=timing_code;
code_lenth_ptr=timing_code_lenth;
t=timing_code_lenth[1];
temp=timing_code[1];
key_up=0; //缺省为按键抬起状态
key_down=0;
last_key=1;
//**************************主循环************************
while(1)
{
RTCCFG|=0x03; //置指针为0b11
Rtimelo[0]=RTCVALL; //读年
Rtimehi[0]=RTCVALH; //读空
Rtimelo[1]=RTCVALL; //读日
Rtimehi[1]=RTCVALH; //读月
Rtimelo[2]=RTCVALL; //读小时
Rtimehi[2]=RTCVALH; //读星期
Rtimelo[3]=RTCVALL; //读秒
Rtimehi[3]=RTCVALH; //读分
hour_colour=Rtimelo[2]>>4; //十小时颜色
hour=Rtimelo[2]&0x0f; //小时
minut_colour=Rtimehi[3]>>4; //十分颜色
minut=Rtimehi[3]&0x0f; //分钟
timing_code[1]=morse_code[hour]; //小时的码型
timing_code_lenth[1]=code_lenth[hour]; //小时的码长
timing_code[2]=morse_code[minut]; //分的码型
timing_code_lenth[2]=code_lenth[minut]; //分的码长
//以下为修改时钟的键操作部分
if (key_status==0)
{
hour_colour=6; //白色光
minut_colour=0;
for (i=0;i<3;i++)
timing_code[i]=0xffff; //全长划,表示进入调整时钟模式
while (key_status==0);
for (i=0;i<3;i++)
timing_code[i]=0;
while (key_status) //一次长按键之前,修改十小时值
{
timing_code[1]=morse_code[Rtimelo[2]>>4]; //十小时
timing_code_lenth[1]=code_lenth[Rtimelo[2]>>4];
timing_code[2]=morse_code[Rtimelo[2]&0x0f]; //小时
timing_code_lenth[2]=code_lenth[Rtimelo[2]&0x0f];
if (key_up==1) //如果发现短时间按键的抬键信号
{
Rtimelo[2]+=0x10; //步进十小时
key_up=0; //清除短时间按键标志
}
if (Rtimelo[2]>0x23)
Rtimelo[2]=0;
}
while (key_status==0) key_up=0; //防止一次按键连续动作
hour_colour=0; //lan色光
minut_colour=6; //白色光
while (key_status) //一次长按键之前,修改小时值
{
timing_code[1]=morse_code[Rtimelo[2]>>4]; //十小时
timing_code_lenth[1]=code_lenth[Rtimelo[2]>>4];
timing_code[2]=morse_code[Rtimelo[2]&0x0f]; //小时
timing_code_lenth[2]=code_lenth[Rtimelo[2]&0x0f];
if (key_up==1) //如果发现短时间按键的抬键信号
{
Rtimelo[2]++; //步进小时
key_up=0; //清除短时间按键标志
}
if (Rtimelo[2]>0x23) //总小时数若大于23,则小时个位置0
Rtimelo[2]=0x20;
if ((Rtimelo[2]&0x0f)>9) //小时值若大于9
Rtimelo[2]&=0xf0; //修改为0
}
while (key_status==0) key_up=0; //防止一次按键连续动作
//以下修改分钟
hour_colour=6; //白色光
minut_colour=0; //蓝色光
while (key_status) //一次长按键之前,修改十分钟值
{
timing_code[1]=morse_code[Rtimehi[3]>>4]; //十分钟
timing_code_lenth[1]=code_lenth[Rtimehi[3]>>4];
timing_code[2]=morse_code[Rtimehi[3]&0x0f]; //分钟
timing_code_lenth[2]=code_lenth[Rtimehi[3]&0x0f];
if (key_up==1) //如果发现短时间按键的抬键信号
{
Rtimehi[3]+=0x10; //步进十分钟
key_up=0; //清除短时间按键标志
}
if (Rtimehi[3]>0x59) //总fen数若大于59,则十分钟位置0
Rtimehi[3]&=0x0f;
}
while (key_status==0) key_up=0; //防止一次按键连续动作
hour_colour=0; //蓝色光
minut_colour=6; //白色光
bwhile (key_status) //一次长按键之前,修改分钟值
{
timing_code[1]=morse_code[Rtimehi[3]>>4]; //十分钟
timing_code_lenth[1]=code_lenth[Rtimehi[3]>>4];
timing_code[2]=morse_code[Rtimehi[3]&0x0f]; //分钟
timing_code_lenth[2]=code_lenth[Rtimehi[3]&0x0f];
if (key_up==1) //如果发现短时间按键的抬键信号
{
Rtimehi[3]++; //步进分钟
key_up=0; //清除短时间按键标志
}
if ((Rtimehi[3]&0x0f)>9) //fen数个位若大于9,则分钟位置0
Rtimehi[3]=Rtimehi[3]&0xf0;
}
while (key_status==0) key_up=0; //防止一次按键连续动作
//写入RTCC
RTCCFG|=0x03; //置指针为0b11
while (RTCCFGbits.RTCSYNC); //等待不忙
for (i=0;i<4;i++) //写
{
RTCVALL=Rtimelo[i];
RTCVALH=Rtimehi[i];
}
}
//到达MAIN最后部分
}
}
//----------------------------------------------------------------------------
// High priority interrupt vector
#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh (void)
{
_asm
goto InterruptHandlerHigh //jump to interrupt routine
_endasm
}
//----------------------------------------------------------------------------
// High priority interrupt routine
#pragma code
#pragma interrupt InterruptHandlerHigh
void
InterruptHandlerHigh ()
{
if (INTCONbits.TMR0IF)
{ //check for TMR0 overflow
INTCONbits.TMR0IF = 0; //clear interrupt flag
TMR0H=0X20; //略加快点划速率
TMR0L=0;
if ((t<=0)|t>25)
{
if (morse_ptr>&timing_code[2])
{
morse_ptr=timing_code;
code_lenth_ptr=timing_code_lenth;
}
else
{
morse_ptr++;
code_lenth_ptr++;
}
if (morse_ptr>(&timing_code[1])) //小时在1,分在2,据此决定in_hour
in_hour=0;
else
in_hour=1;
if (in_hour==0) //十分钟颜色
{
if (minut_colour==0) //蓝
{
LEDG=1;
LEDR=1;
LEDB=0;
}
else if (minut_colour==1) //绿
{
LEDG=0;
LEDR=1;
LEDB=1;
}
else if (minut_colour==2) //黄
{
LEDG=0;
LEDR=0;
LEDB=1;
}
else if (minut_colour==3) //红
{
LEDG=1;
LEDR=0;
LEDB=1;
}
else if (minut_colour==4) //青
{
LEDG=0;
LEDR=1;
LEDB=0;
}
else if (minut_colour==5) //紫
{
LEDG=1;
LEDR=0;
LEDB=0;
}
else //白
{
LEDG=0;
LEDR=0;
LEDB=0;
}
}
else //十小时颜色
{
if (hour_colour==0) //蓝
{
LEDG=1;
LEDR=1;
LEDB=0;
}
else if (hour_colour==1) //绿
{
LEDG=0;
LEDR=1;
LEDB=1;
}
else if (hour_colour==2) //黄
{
LEDG=0;
LEDR=0;
LEDB=1;
}
else if (hour_colour==3) //红
{
LEDG=1;
LEDR=0;
LEDB=1;
}
else if (hour_colour==4) //青
{
LEDG=0;
LEDR=1;
LEDB=0;
}
else if (hour_colour==5) //紫
{
LEDG=1;
LEDR=0;
LEDB=0;
}
else //白
{
LEDG=0;
LEDR=0;
LEDB=0;
}
}
temp=*morse_ptr; //取得指针位置的值
t=*code_lenth_ptr; //同上
}
else
{
t--;
}
if ((temp&0x8000)==0) //这段是拍发算盘码的执行部分
LATB = LATB|0b00001101;
else
LATB = LATB&0b1110010;
temp=temp<<1;
}
else if (PIR5bits.TMR5IF) //该定时器用来识别按键动作
{
PIR5bits.TMR5IF = 0; //clear interrupt flag
if (key_down==1&last_key==1) //只有下键后才进入判断
{
if (key_status) //在没有全0序列情况下
if (Key==1) //确认抬键
{
key_up=1;
key_down=0;
}
}
else if (last_key==0)
{
if (Key==0)
{
key_down=1; //确认下键
key_up=0;
}
}
last_key=Key; //当前键值赋予旧键值
key_status=key_status<<1; //键状态左移1位
key_status|=Key; //末位置Key的值
}
}