File "horloge.c"
Full Path: /home/analogde/www/php/Sources/horloge.c
File size: 12.99 KB
MIME-type: text/x-c
Charset: 8 bit
/*
Commandes Afficheur LCD RS (Register Select) : 0 -> Instr. Reg.
* 1 -> Data Reg.
bitwise
rtc.c realtime controller
2-Channel Time Switch V1.0
Motorola MC908QY4CP 16-pin MCU with external XTAL 4MHz for
timebase realtime clock
compiled with icc08
Copyright (c) 2004 Wichit Sirichote, kswichit@kmitl.ac.th
mode settings state
0 set hour and min NA
1 set day NA
2 manual on/off NA
3 enter scheduler 0 :enter time 1 :enter day 2: set output
4 display scheduler NA
*/
#include <io908QY4.H>
#define key0 (PTA&0x01)
#define key1 (PTA&0x02)
#define key2 (PTA&0x04)
#define key3 (PTA&0x08)
char temp,k,mode;
char i,n,timer1,timer2,flag;
unsigned char sec100,sec,min,hour,day,output;
unsigned char sec, warm_boot;
char *titre = "Horloge V1.0";
char buffer[8];
char *day_str[] = {"AA","MO","TU","WE","TH","FR","SA","SU","__"};
// "AA" for all days control
// "__" for end of program
char state;
struct pgm{
char set_day;
char set_hour;
char set_min;
char set_control;
};
struct pgm PGM[5];
/* signal for lcd interface
E PTB1
RS PTB0
D4 PTB4
D5 PTB5
D6 PTB6
D7 PTB7
D0-D3 and R/W# are tied to GND
*/
// function prototype declarations
void commande_LCD(char c);
void LCDWD(char c);
void print_dec(char d,unsigned int n);
void print_time(char d);
void tick_display(void);
void rafraichir_horloge(void);
void scrute_clavier(void);
void print_output(char n);
void print_record(char d,char n);
void compare_time(void);
void fire_output(char N);
void initialisation_LCD(void);
// enter timer overflow every 1/100s or 10ms
#pragma interrupt_handler isr_TIM
void isr_TIM(void)
{
// fonction de gestion de l'interruption
//PTB ^= 0x08; // toggle PTB.3 to check period
// found period was 20.012ms
TSC &= ~0x80; // clear TOF
flag |= 2; // tick 1/100Hz
COPCTL = 0; // clear COP
rafraichir_horloge();
tick_display(); // tick every 0.5 sec
scrute_clavier();
}
#pragma interrupt_handler isrDummy
void isrDummy(void)
{
COPCTL = 0; // clear COP
}
#pragma abs_address:0xffde
void (* const _vectab[])(void) = {
isrDummy, /* ADC */
isrDummy, /* KEYBOARD */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isrDummy, /* NOT USED */
isr_TIM, /* TIM OVERFLOW */
isrDummy, /* TIM1 */
isrDummy, /* TIM0 */
isrDummy, /* NOT USED */
isrDummy, /* IRQ */
isrDummy /* SWI */
};
#pragma end_abs_address
/********************************************************************/
void rafraichir_horloge()
{
if(++sec100 >=100)
{
sec100 = 0;
if(mode !=3 && mode !=4)
{
print_time(0x80);
print_output(output);
}
if(++sec > 59)
{
sec = 0;
compare_time();
if( ++min >59)
{
min = 0;
if(++hour >23)
{
hour = 0;
if(++day > 7)
day = 1;
}
}
}
}
}
/********************************************************************/
void tick_display()
{
if(mode !=3 && mode !=4)
{
if(++timer1> 50)
{
timer1 = 0;
commande_LCD(0x85); // swap display
k^=1;
if (k) LCDWD(' ');
else LCDWD(':');
}
}
}
/********************************************************************/
void reglage_heure()
{
++heure;
if(heure>23)
{ heure=0; }
affiche_heure(0x80);
}
/********************************************************************/
void reglage_minute()
{
++min;
sec = 0;
compare_time();
if(min>59)
{ min =0; }
affiche_heure(0x80);
}
/********************************************************************/
void reglage_jour()
{
++jour;
if(jour>7)
{ day = 1; }
affiche_heure(0x80);
}
/********************************************************************/
void output2_setting()
{
output^=2; // XOR si x=6 alors x^=3 -> x=x^3
// 0110
// 0011
// 0101
print_output(output);
}
/********************************************************************/
void output1_setting()
{
output^=1;
print_output(output);
}
/********************************************************************/
void pgm_setting1()
{
switch(state)
{
case 0:
temp =PGM[n].set_hour;
++temp;
if(temp>23) temp = 0;
PGM[n].set_hour = temp;
print_record(0x80,n);
break;
case 1:
temp = PGM[n].set_day;
++temp;
if(temp>8) temp = 0;
PGM[n].set_day = temp;
print_record(0x80,n);
break;
case 2:
PGM[n].set_control ^= 2;
print_record(0x80,n);
break;
}
}
/********************************************************************/
void pgm_setting2()
{
switch(state)
{
case 0:
temp =PGM[n].set_min;
++temp;
if(temp>59) temp = 0;
PGM[n].set_min = temp;
print_record(0x80,n);
break;
case 2:
PGM[n].set_control ^= 1;
print_record(0x80,n);
break;
}
}
/********************************************************************/
void traite_touche2()
{
switch(mode)
{
case 0: hour_setting(); break;
case 1: day_setting(); break;
case 2: output2_setting();break;
case 3: pgm_setting1(); break;
case 4: ++n; if(n>4) n=0; print_record(0x80,n);
commande_LCD(0xc7); // mode 4 shows record number
LCDWD(n+48); break;
}
}
/********************************************************************/
void servicekey3()
{
switch(mode)
{
case 0: min_setting(); break;
case 2: output1_setting();break;
case 3: pgm_setting2(); break;
}
}
/********************************************************************/
void servicekey0()
{
switch(mode)
{
case 0:i_LCD(); // reinit lcd manually
break;
case 3:
++state;
if(state == 3)
{
state = 0; // back to initial sequence
++n; // next record
}
if(n>5) n = 0; // loop back to record 0
print_record(0x80,n);
commande_LCD(0xc7); LCDWD(state+48);
break;
}
}
/********************************************************************/
// use bit 0 and bit 1 for two bits output at port B
void print_output(char m)
{
commande_LCD(0xc1);
if(m&2) LCDWD('*');
else LCDWD('-');
commande_LCD(0xc3);
if(m&1) LCDWD('*');
else LCDWD('-');
fire_output(m); // send to PTB also
}
/********************************************************************/
void servicekey1()
{
++mode;
if(mode>4) mode = 0;
commande_LCD(0xc5);
LCDWD(mode+48);
LCDWD('.');
LCDWD(state+48); // mode 3 has more state
switch(mode)
{
case 3:
n=0;
print_record(0x80,n);
state = 0;
break;
case 4:
n=0;
state = 0;
print_record(0x80,n); // print record 0
commande_LCD(0xc7); // mode 4 shows record number
LCDWD(n+48);
break;
}
}
/********************************************************************/
void scrute_clavier()
{ // controle l'appui sur une touche toutes les 250ms
if(++timer2 > 20)
{
timer2 = 0;
if(touche0 == 0) traite_touche0();
if(touche1 == 0) traite_touche1();
if(touche2 == 0) traite_touche2();
if(touche3 == 0) traite_touche3();
}
}
/********************************************************************/
void tempo(int j)
{
unsigned int i;
for (i = 0; i < j; i++)
COPCTL = 0; // clear COP otherwise the cpu itself will be reset
}
/********************************************************************/
void selection_LCD()
{
// on utilise la pin ENABLE de l'afficheur
PORTB |= 2; // mettre le bit1=1
tempo(1);
PORTB &= ~2; // mettre le bit1=0
}
/********************************************************************/
void commande_LCD(char c)
{
/* crire dans le registre d'instruction */
char temp;
temp = c;
PTB &= ~3; // clear RS and E
PTB &= 0x0f; // clear high nibbel to low
c &= 0xf0; // prepare only high nibble
PTB |= c;
pulseE();
c = temp;
c <<=4;
PTB &= 0x0f;
PTB |= c;
pulseE();
delay(5);
}
/********************************************************************/
void data_LCD(char c)
{
/* crire dans le registre de donnes */
char temp;
temp = c;
PTB |= 1; // set bit RS
PTB &= ~2; // clear bit E
PTB &= 0x0f; // clear high nibbel to low
c &= 0xf0; // prepare only high nibble
PTB |= c; // mearge high nibble first
pulseE();
c = temp;
c <<=4;
PTB &= 0x0f;
PTB |= c;
pulseE();
delay(5);
}
/********************************************************************/
void initalisation_LCD()
/* circuit standard Hitachi HD44780 mode 4 bits */
{
PORTB &= ~3; // clear RS and E
PORTB |= 0x30;
pulseE();
delay(200);
pulseE();
delay(50);
pulseE();
delay(50);
PORTB &= 0x0f; // et logique
PORTB |= 0x20; // ou logique
pulseE();
pulseE();
pulseE();
commande_LCD(0x28); // passe le bus en mode 4bits, 1ligne- 16caractres, 5*7 points
commande_LCD(0x0c); // display on/off on display,off cursor, no blink
commande_LCD(0x06); // entry mode DDRAM auto address increment
commande_LCD(1); // clear display
delay(50);
0x28 mode 4 bits, 2 lignes, caractre 5x8
LDAA #%00001110 Display ON, Cursor ON, Blink OFF
BSR LCD_CMD
LDAA #%00000110 Auto Inc, Cur. shift R, no Disp shift
BSR LCD_CMD
}
/********************************************************************/
// print LCD text >8
// the 16x1 line LCD has two blocks start address!
void affiche_chaine(char *chaine)
{
char i;
commande_LCD(0x01); // efface LCD
commande_LCD(0x80); // fixe la position de depart du curseur
for(i=0; i<8; i++)
LCDWD(*s++);
commande_LCD(0xc0); // new address for position 9
for(i=0; i<8; i++)
LCDWD(*s++);
}
/********************************************************************/
void affiche_horloge(char d)
{
// affiche l'heure sur l'afficheur en 0x80 or 0xc0
buffer[0] = *day_str[day];
buffer[1] = *(day_str[day]+1);
buffer[2] = ' ';
buffer[3] = hour/10+48;
if(buffer[3] =='0') buffer[3] = ' ';
buffer[4] = hour%10+48;
// buffer[5] = ':'; left for tick display
buffer[6] = min/10+48;
buffer[7] = min%10+48;
commande_LCD(d);
for(i=0; i<5; i++)
LCDWD(buffer[i]);
commande_LCD(0x86);
LCDWD(buffer[6]);
LCDWD(buffer[7]);
}
// print record number #n on LCD at location d
/********************************************************************/
void print_record(char d,char n)
{
buffer[0] = *day_str[PGM[n].set_day];
buffer[1] = *(day_str[PGM[n].set_day]+1);
buffer[2] = ' ';
buffer[3] = PGM[n].set_hour/10+48;
if(buffer[3] =='0') buffer[3] = ' ';
buffer[4] = PGM[n].set_hour%10+48;
buffer[5] = ':';
buffer[6] = PGM[n].set_min/10+48;
buffer[7] = PGM[n].set_min%10+48;
commande_LCD(d);
for(i=0; i<8; i++)
LCDWD(buffer[i]);
print_output(PGM[n].set_control);
}
/********************************************************************/
// sink current driving, fire with logic '0'
void fire_output(char N)
{
if(N&1) PTB &= ~0x08; // clear PTB.3
else PTB |= 0x08; // set PTB.3
if(N&2) PTB &= ~0x04; // clear PTB.2
else PTB |= 0x04; // set PTB.2
}
/********************************************************************/
// scan 5 records, compare
// if matched, fires output
void compare_time()
{
char i;
for(i=0; i<5; i++)
{
if(PGM[i].set_day != 8) // skip comapre time if day = 8
{
if(PGM[i].set_day == 0) // daily control
{
if(PGM[i].set_hour == hour && PGM[i].set_min == min)
{
output=PGM[i].set_control;
print_output(output);
}
}
else
{ // check day also
if(PGM[i].set_day == day && PGM[i].set_hour == hour && PGM[i].set_min == min)
{
output = PGM[i].set_control;
print_output(output);
}
}
}
}
}
/********************************************************************/
void main()
{
// attend la fin du reset materiel : par exemple 100ms
delay(1000); // tempo dmarage du LCD
DDRB = 0xff; // configuration port B en output
PORTB = 0x30; // clear port B
delay(1000);
n = k=0;
TMODH = 0x04;
TMODL = 0xe0; //0xe2; // for 100Hz generation!
// value in TMODL can be adjusted for precise timing
TSC = 0x43; // run timer with interrupt
// 4Mhz/4 = 1Mhz/8 = 125000Hz
DDRA = ~0xf; // PA0-PA3 are input
PTA = 0xff; // drive oscillator high
// DDRB = 0xff; // port B is output port
// PTB = 0; // clear port B
// PTAPUE = ~0x81; // osc2 pin is PTA4 I/O
//ADCLK = 0x40; // ADC clock= fbus/4
initialisation_LCD();
PTAPUE = 0x0f; // enable internal pullup
CONFIG2 = 0x18; // external XTAL osc!
delay(10);
OCSTAT = 0x02; // enable external clock!
affiche_chaine(titre);
delay(20000);
commande_LCD(1); // clear lcd
// skip initial load if warm boot == '%'
if(warm_boot != '%')
{
warm_boot = '%';
day = 1;
hour = 10;
min = 40;
sec = 0;
}
asm cli; // active les irqs
for(;;)
{
COPCTL = 0; // clear COP
}
}
/********************************************************************/