// Chapter 8 9S12C32 C programs // Jonathan W. Valvano, 2/07/07 // This software accompanies the book, // Embedded Microcomputer Systems: Real Time Interfacing, Second Edition // published by Thomson Engineering, 2006 // MC9S12C32 // PortAD bit 1 is connected to a switch to +5, using internal pull-down // PortAD bit 0 is connected to a switch to 0, using internal pull-up void PortAD_Init(void){ ATDDIEN |= 0x03; // PAD1-0 digital I/O DDRAD &= ~0x03; // PAD1-0 inputs PPSAD |= 0x02; // pull-down on PAD1 PPSAD &= ~0x01; // pull-up on PAD0 PERAD |= 0x03; // enable pull-up and pull-down } //Program 8.1. The MC9S12C32 Port AD initialization. // MC9S12C32 void Key_WaitPress(void){ while(PTT&0x08); // PT3=0 when pressed Timer_Wait10ms(1); // debouncing } void Key_WaitRelease(void){ while((PTT&0x08)==0); // PT3=1 -> released Timer_Wait10ms(1); // debouncing } void Key_Init(void){ Timer_Init(); DDRT &=~0x08; // PT3 is input } //Program 8.3. Switch debouncing using C software. // MC9S12C32, PT3 is input // waits until unchanged for 10ms unsigned char Key_Read(void){ unsigned char old; old = PTT&0x08; // Current value TC5 = TCNT+20000; // 10ms delay TFLG1 = 0x20; // Clear C5F while((TFLG1&0x20)==0){ // 10ms? if(old<>(PTT&0x08)){ // changed? old = PTT&0x08; // New value TC5 = TCNT+20000; // restart delay } } return(old); } void Key_Init(void) { TIOS |= 0x20; // enable OC5 TSCR1 = 0x80; // enable TSCR2 = 0x01; // 500 ns clock DDRT &=~0x08; // PT3 is input } //Program 8.5. Another example of switch debouncing using C software. // MC9S12C32, CodeWarrior C // counts the number of button pushes // button connected to IC3=PT3 unsigned short Count; // times pushed void interrupt 13 TC5handler(void){ TIE |= 0x08; // arm IC3, TIE &=~0x20; // disarm OC5 if(PTT&0x08==0) Count++; TFLG1 = 0x28; // clear C5F,C3F } void interrupt 11 TC3handler(void){ TIE &=~0x08; // disarm IC3, TIE |= 0x20; // arm OC5 TC5 = TCNT+20000; // 10 ms TFLG1 = 0x28; // clear C5F,C3F } void Key_Init(void){ asm sei // make atomic TIOS=(TIOS&0xF7)|0x20; // enable OC5,IC3 TSCR1 = 0x80; // enable TSCR2 = 0x01; // 500 ns clock DDRT &=~0x08; // PT3 is input TIE |= 0x08; // arm IC3, TIE &=~0x20; // disarm OC5 TFLG1 = 0x28; // clear C5F,C3F TCTL4 = (TCTL4&0x3F)|0x40; // rising Count = 0; asm cli } //Program 8.6. Switch debouncing using interrupts in C software. // MC9S12C32, CodeWarrior C // counts the number of button pushes // signal connected to IC3=PT3 unsigned short Count; // times pushed char LastState; // looking for touch? // true means open, looking for a touch // false means closed, looking for release void interrupt 13 TC5handler(void){ TIE |= 0x08; // arm IC3, TIE &=~0x20; // disarm OC5 TFLG1 = 0x28; // clear C5F,C3F } void interrupt 11 TC3handler(void){ if(LastState){ // every other Count++; // a touch has occurred LastState=0;} else LastState=1; // release occurred TIE &=~0x08; // disarm IC3, TIE |= 0x20; // arm OC5 TC5 = TCNT+20000; // 10 ms TFLG1 = 0x28; // clear C5F,C3F } void Key_Init(void){ asm sei // make atomic TIOS=(TIOS&0xF7)|0x20; // enable OC5,IC3 TSCR1 = 0x80; // enable TSCR2 = 0x01; // 500 ns clock DDRT &=~0x08; // PT3 is input TIE |= 0x08; // arm IC3, TIE &=~0x20; // disarm OC5 TFLG1 = 0x28; // clear C5F,C3F TCTL4 |= 0xC0; // both edges Count = 0; LastState = PTT&0x08; asm cli } //Program 8.7. Low latency switch debouncing using interrupts in C software. // MC68HC812A4, ICC12 C unsigned short Key; // current pattern void KeyBoard(void){ KWIFH = KWIFJ = 0xFF; // clear flags Key = (PORTJ<<8)+PORTH;} // Set global void Key_Init(void){ asm(" sei"); // make atomic TIOS |= 0x20; // enable OC5 TSCR = 0x80; // enable TMSK2 = 0x32; // 500 ns clock TMSK1 &= 0xDF; // Disarm OC5 DDRH = DDRJ = 0; // H,J are key inputs KWIEH=KWIEJ=0xFF; // all 16 are armed KPOLJ = 0; // falling edge PULEJ = 0; // regular input KeyBoard(); // Initially read asm(" cli"); } #pragma interrupt_handler KeyHhandler() void KeyHhandler(void){ KWIEH=KWIEJ=0x00; // all 16 are disarmed TMSK1 |= 0x20; // Arm OC5 TC5 = TCNT+20000; // 10 ms TFLG1 = 0x20;} // clear C5F #pragma interrupt_handler KeyJhandler() void KeyJhandler(void){ KWIEH=KWIEJ=0x00; // all 16 are disarmed TMSK1 |= 0x20; // Arm OC5 TC5 = TCNT+20000; // 10 ms TFLG1 = 0x20;} // clear C5F #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TMSK1 &= 0xDF; // Disarm OC5 KWIEH=KWIEJ=0xFF; // all 16 are rearmed KeyBoard();} // Read keys //Program 8.9. C software interface of a direct connection keyboard. // MC68HC812A4, ICC12 C unsigned short Key; // current pattern unsigned short KeyBoard(void){ return((PORTH<<8)+PORTJ);} // pattern void Key_Init(void){ asm(" sei"); // make atomic TIOS |= 0x20; // enable OC5 TSCR = 0x80; // enable TMSK2 = 0x32; // 500 ns clock TMSK1 |= 0x20; // Arm OC5 DDRH = DDRJ = 0; // H,J are key inputs PULEJ=0; // regular input Key = KeyBoard(); // read 16 keys TC5 = TCNT+20000; asm(" cli"); } #pragma interrupt_handler TOC5handler() void TOC5handler(void){ Key = KeyBoard(); // Current pattern TC5 = TC5+20000; // every 10ms TFLG1 = 0x20;} // ack OC5F //Program 8.10. C software interface of a direct connection keyboard using periodic polling. // MC9S12C32 const struct Row { unsigned char direction; unsigned char keycode[4];} typedef const struct Row RowType; RowType ScanTab[5]={ { 0x80, "abcd" }, // row 3 { 0x40, "efgh" }, // row 2 { 0x20, "ijkl" }, // row 1 { 0x10, "mnop" }, // row 0 { 0x00, " " }}; void Key_Init(void){ DDRT = 0x00; // PT3-PT0 inputs PTT = 0; // PT7-PT4 oc output PPST = 0 // pull-up on PT3-PT0 PERT = 0x0F;} /* Returns ASCII code for key pressed, Num is the number of keys pressed both equal zero if no key pressed */ unsigned char Key_Scan(short *Num){ RowType *pt; unsigned char column,key; short j; (*Num)=0; key=0; // default values pt=&ScanTab[0]; while(pt->direction){ DDRT = pt->direction; // one output column = PTT; // read columns for(j=3; j>=0; j--){ if((column&0x01)==0){ key = pt->keycode[j]; (*Num)++;} column>>=1;} // shift into position pt++; } return key;} //Program 8.12. C software interface of a matrix scanned keyboard. // MC68HC812A4 // PJ7-PJ4 row output // PJ3-PJ0 column inputs unsigned char Key; // current pattern unsigned char PreviousKey; // 10ms ago #define period 20000 // 10 ms unsigned char KeyScan(void){ unsigned char key,row; key=0; // means no key pressed for(row=0;row<16;row++){ PORTJ=row<<4; // Select row if((PORTJ&0x0F)!=0x0F){ key=PORTJ^0x0F; }} return(key);} void Ritual(void){ asm(" sei"); // make ritual atomic DDRJ=$F0; PreviousKey=Key=KeyScan(); // read TMSK1|=0x20; // Arm OC5 TIOS|=OC5; // enable OC5 TSCR|=0x80; // enable TMSK2=0x32; // 500 ns clock TC5=TCNT+wait; TFLG1=0x20; // clear OC5F asm(" cli"); } #pragma interrupt_handler TOC5handler() void TOC5handler(void){ unsigned char NewKey; NewKey=KeyScan(); // Current pattern if(NewKey==PreviousKey) Key=NewKey; PreviousKey=NewKey; TOC5=TOC5+period; TFLG1=0x20;} // ack OC5F // Program 8.14. C software interface of a multiplexed keyboard. // MC9S12C32, CodeWarrior C // PT7-PT0 output, 7 bit pattern // PM2-PM0 output, selects LED digit unsigned char code[3]; // binary codes static unsigned char select[3]={4,2,1}; unsigned short index; // 0,1,2 void interrupt 13 TC5handler(void){ TFLG1 = 0x20; // Acknowledge TC5 = TC5+10000; // every 5 ms PTM = select[index]; // which LED? PTT = code[index]; // enable if(++index==3) index=0;} void LED_Init(void) { asm sei // make atomic index = 0; DDRT = 0xFF; // outputs 7 segment code DDRM |= 0x03; // outputs select LED TIE |=0x20; // Arm OC5 TIOS |=0x20; // enable OC5 TSCR1 =0x80; // enable TSCR2 =0x01; // 500 ns clock TC5 = TCNT+10000; asm cli } //Program 8.15. C software interface of a scanned LED display. // MC9S12C32, CodeWarrior C unsigned short Global; // 12-bit packed BCD const struct LED { unsigned char enable; // select unsigned char shift; // bits to shift const struct LED *Next; }; // Link typedef const struct LED LEDType; typedef LEDType * LEDPtr; LEDType LEDTab[3]={ { 0x04, 8, &LEDTab[1] }, // Most sig { 0x02, 4, &LEDTab[2] }, { 0x01, 0, &LEDTab[0] }}; // least sig LEDPtr Pt; // Points to current digit void interrupt 13 TC5handler(void){ TFLG1 = 0x20; // Acknowledge TC5 = TC5+10000; // every 5 ms PTT = (Pt->enable) +(Global>>(pt->shift))<<4); Pt = Pt->Next; } void LED_Init(void) { asm sei // make atomic DDRT = 0xFF; // outputs to LED's Global = 0; Pt=&LEDTab[0]; TIE |= 0x20; // Arm OC5 TIOS |= 0x20; // enable OC5 TSCR1 = 0x80; // enable TSCR2 = 0x01; // 500 ns clock TC5 = TCNT+10000; asm cli } //Program 8.16. C software interface of a multiplexed LED display. // 9S12C32 // PM4/MOSI = MC14489 DATA IN // PM5/SCLK = MC14489 CLOCK IN // PM3 (simple output) = MC14489 ENABLE void LED_Init(void) { DDRM |= 0x38; // outputs to MC14489 SPICR1 = 0x50; // bit meaning // 7 SPIE=0 no interrupts // 6 SPE=1 SPI enable // 5 SPTIE=0 no interrupts // 4 MSTR=1 master // 3 CPOL=0 match timing with MC14489 // 2 CPHA=0 // 1 SSOE=0 PM3 is simple output // 0 LSBF=0 MSB first SPICR2 = 0x00; // regular drive SPIBR = 0x01; // 1MHz SCLK PTM |= 0x08; // ENABLE=1 PTM &=~0x08; // ENABLE=0 SPIDR= 0x01; // hex format while(SPISR&0x80)==0){}; PTM |=0x08;} // ENABLE=1 void LED_out(unsigned char data[3]){ unsigned char dummy; PTM &=~0x08; // ENABLE=0 while((SPISR&SPTEF)==0); // wait for transmit empty SPIDR = data[2]; // send MSbyte dummy = SPIDR; // clear SPIF while((SPISR&SPTEF)==0); // wait for transmit empty SPIDR = data[1]; // send middle byte dummy = SPIDR; // clear SPIF while((SPISR&SPTEF)==0); // wait for transmit empty SPIDR = data[0]; // send LSbyte dummy = SPIDR; // clear SPIF Timer_Wait(10); // wait for SPI output completion PTM |=0x08;} // ENABLE=1 //Program 8.17. C software interface of an integrated LED display. void LCDOutDigit(unsigned char position, unsigned char data) { // position is 0x80, 0x40, 0x20, or 0x10 and data is the BCD digit PORTB = 0x0F&data; // set BCD digit on the A-D inputs of the MC14543B PORTB |= position; // toggle one of the LD inputs high PORTB = 0x0F&data; // LD=0, latch digit into MC14543B } //Program 8.18. Helper function for a simple LCD display. void LCD_OutNum(unsigned short data){ unsigned short digit,num,i; unsigned char pos; num = min(data,9999); // data should be unsigned from 0 to 9999 pos = 0x10; // position of first digit (ones) for(i=0;i<4;i++){ digit = num%10; num = num/10; // next BCD digit 0 to 9 LCDOutDigit(pos,digit); pos = pos<<1; } } //Program 8.19. C software interface of a simple LCD display. void LCD_Out (unsigned char *pt) {unsigned short i; unsigned char mask; for(i=0;i<6;i++){ for(mask=0x80;mask;mask=mask>>1){ // look at bits 7,6,5,4,3,2,1,0 if((*pt)&mask) PORTB=1; else PORTB=0; // Serial data of the MC145000 PORTB |= 0x02; // toggle the serial clock first high PORTB &=~0x02; // then low } pt++; } } //Program 8.20. Bit-banged interface to a scanned LCD display. // MC9S12C32 // PM4/MOSI = MC145000 DATA IN // PM5/SCLK = MC145000 CLOCK IN void LCD_Init(void) { DDRS |= 0x30; // outputs to MC145000 SPICR1 = 0x50; // bit meaning // 7 SPIE=0 no interrupts // 6 SPE=1 SPI enable // 5 SPTIE=0 no interrupts // 4 MSTR=1 master // 3 CPOL=0 match timing with MC14489 // 2 CPHA=0 // 1 SSOE=0 PM3 is simple output // 0 LSBF=0 MSB first SPICR2 = 0x00; // regular drive SPIBR = 0x01;} // 1MHz SCLK void LCD_out(unsigned char data[6]){ unsigned short j; unsigned char dummy; for(j=5; j>=0 ; j--){ while((SPISR&SPTEF)==0); // wait for transmit empty SPIDR = data[j]; // Msbyte first dummy = SPIDR; // clear SPIF } } //Program 8.21. SPI interface to a scanned LCD display using a MC145000. // MC9S12C32 // 1 by 16 char LCD Display (HD44780) // ground = pin 1 Vss // power = pin 2 Vdd +5v // 10kpot = pin 3 Vlc contrast adjust // PM2 = pin 6 E enable // PM1 = pin 5 R/W 1=read, 0=write // PM0 = pin 4 RS 1=data, 0=control // PT0-7 = pins7-14 DB0-7 8-bit data static void outCsr(unsigned char command){ PTT = command; PTM &=~0x03; // RS=0, R/W=0 PTM |= 0x04; // E=1 PTM &=~0x04; // E=0 Timer_Wait(80);} // Program 2.6 //Program 8.22. Private functions for an HD44780 controlled LCD display. // MC9S12C32 void LCD_OutChar(unsigned char letter){ PTT = letter; // ASCII code PTM |= 0x01; // RS=1, R/W=0 PTM |= 0x04; // E=1 PTM &=~0x04; // E=0 Timer_Wait(80);} // 40 us wait void LCD_Clear(void){ outCsr(0x01); // Clear Display Timer_Wait(3280); // 1.64ms wait outCsr(0x02); // Cursor to home Timer_Wait(3280);} // 1.64ms wait void LCD_Init(void){ DDRT = 0xFF; DDRM |=0x03; PTM &=~0x02; // R/W=0 Timer_Init(); // Program 2.6 outCsr(0x06); // Incr,nodisplayshift outCsr(0x0C); // on,cursoroff,blinkoff outCsr(0x14); // cursormove,shiftright outCsr(0x30); // 8bit,1line,5by7dots LCD_Clear(); // clear display } //Program 8.23. Public functions for an HD44780 controlled LCD display. // 6811 or 6812 const struct State{ unsigned char Out; // Output const struct State *Next[2]; // CW/CCW }; typedef struct State StateType; typedef StateType *StatePtr; #define clockwise 0 // Next index #define counterclockwise 1 // Next index StateType fsm[4]={ {10,{&fsm[1],&fsm[3]}}, { 9,{&fsm[2],&fsm[0]}}, { 5,{&fsm[3],&fsm[1]}}, { 6,{&fsm[0],&fsm[2]}} }; unsigned char Pos; // between 0 and 199 StatePtr Pt; // Current State //Program 8.24. A double circular linked list used to control the stepper motor. // 6811 or 6812 // Move 1.8 degrees clockwise void CW(void){ Pt = Pt->Next[clockwise]; // circular PORTB = Pt->Out; // step motor if(Pos==199){ // shaft angle Pos = 0; // reset } else{ Pos++; // CW } } // Move 1.8 degrees counterclockwise void CCW(void){ Pt = Pt->Next[counterclockwise PORTB = Pt->Out; // step motor if(Pos==0){ // shaft angle Pos = 199; // reset } else{ Pos--; // CCW } } // Initialize Stepper interface void Init(void){ Pos = 0; Pt = &fsm[0]; DDRB = 0xFF; // 6812 only } //Program 8.25. Helper functions used to control the stepper motor. // 6811 or 6812 void Seek(unsigned char desired){ short CWsteps; if((CWsteps=desired-Pos)<0){ CWsteps+=200; } // CW steps is 0 to 199 if(CWsteps>100){ while(desired<>Pos){ CCW(); } } else{ while(desired<>Pos){ CW(); } } } //Program 8.26. High-level function to control the stepper motor. //******************************* // old programs from first edition follow // Program 8.1. The MC68HC812A4 Port J initialization. // MC68HC812A4 // PortJ bit 1 is connected to a switch to +5, using internal pull-down // PortJ bit 0 is connected to a switch to 0, using internal pull-up void Initialization(void){ DDRJ &= 0xFC; // PJ1-0 inputs KPOLJ |= 0x03; // flags set on the rise of PJ1, PJ0 KWIEJ &= 0xFC; // disarm PJ1, PJ0 KWIFJ = 0x03; // clear flags PUPSJ = (PUPSJ&0xFC)|0x01; // pull-down on PJ1, pull-up on PJ0 PUPEJ |= 0x03;} // enable pull-up and pull-down // Program 8.3. Switch debouncing using C software. // MC68HC812A4 void WaitPress(void){ while (PORTT&0x$08); // Loop here until switch is pressed TC5=TCNT+20000; // 10ms delay while((TFLG1&0x20)==0);} // wait for switch to stop bouncing void WaitRelease(void){ while ((PORTT&0x$08)==0); // Loop here until switch is released TC5=TCNT+20000; // 10ms delay while((TFLG1&0x20)==0);} // wait for switch to stop bouncing void ritual(void) { TIOS|=0x20; // enable OC5 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock DDRT &= 0xF7;} // PT3 is input // Program 8.5. Another example of switch debouncing using C software. // MC68HC812A4 unsigned char ReadPT3(void){ unsigned char old; old=PORTT&0x08; // Current value TC5=TCNT+20000; // 10ms delay while((TFLG1&0x20)==0){ // unchanged for 10ms? if(old<>(PORTT&0x08)){ // changed? old=PORTT&0x08; // New value TC5=TCNT+20000;} // restart delay } return(old);} void ritual(void) { TIOS|=0x20; // enable OC5 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock DDRT &= 0xF7;} // PT3 is input //Program 8.6. Switch debouncing using interrupts in C software. // MC68HC812A4 // counts the number of button pushes // signal connected to IC3=PT3 unsigned int count; // times pushed #define wait 20000 // bounce wait (cyc) #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TMSK1=0x08; // Arm IC3, disarm OC5 TFLG1=0x08; // clear C3F if(PORTT&0x08==0) count++;} // new count if PT3=0 #pragma interrupt_handler TIC3handler() void TIC3handler(void){ TMSK1=0x20; // Disarm IC3, Arm OC5 TC5=TCNT+wait;} // clear C5F void Ritual(void){ asm(" sei"); // make atomic TIOS=(TIOS&0xF7)|0x20; // enable OC5,IC3 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock DDRT=(DDRT&0xF7)|0x20; // PT3 is input TMSK1=0x08; // Arm IC3, disarm OC5 TFLG1=0x08; // clear C3F TCTL4 = (TCTL4&0x3F)|0x40; // rising count=0; asm(" cli"); } // Program 8.7. Another example of switch debouncing using interrupts in C software. // MC68HC812A4 // counts the number of button pushes // signal connected to IC3=PT3 unsigned int count; // times pushed char LastState; // looking for touch? // true means open, looking for a touch // false means closed, looking for release #define wait 20000 // bounce wait (cyc) #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TMSK1=0x08; // Arm IC3, disarm OC5 TFLG1=0x08;} // clear C3F #pragma interrupt_handler TIC3handler() void TIC3handler(void){ if(LastState){ count++; // a touch has occurred LastState=0;} else LastState=1; // release occurred TMSK1=0x20; // Disarm IC3, Arm OC5 TC5=TCNT+wait;} // clear C5F void Ritual(void){ asm(" sei"); // make atomic TIOS=(TIOS&0xF7)|0x20; // enable OC5,IC3 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock DDRT=(DDRT&0xF7)|0x20; // PT3 is input TMSK1=0x08; // Arm IC3, disarm OC5 TFLG1=0x08; // clear C3F TCTL4 = (TCTL4&0x3F)|0x40; // rising count=0; LastState=PORTT&0x08; asm(" cli"); } // Program 8.9. C software interface of a direct connection keyboard. // MC68HC812A4 unsigned int KEY; // current pattern #define wait 20000 // bounce time (cyc) void Ritual(void){ asm(" sei"); // make atomic TIOS|=0x20; // enable OC5 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock TMSK1&=0xDF; // Disarm OC5 DDRH=DDRJ=0; // H,J are key inputs KWIEH=KWIEJ=0xFF; // all 16 are armed KPOLJ=0; // falling edge PUPEJ=0; // regular input KeyBoard(); // Initially read asm(" cli"); } void KeyBoard(void){ KWIFH=KWIFJ=0xFF; // clear flags KEY=(PORTJ<<8)+PORTH;} // Set global #pragma interrupt_handler KeyHhandler() void KeyHhandler(void){ KWIEH=KWIEJ=0x00; // all 16 are disarmed TMSK1|=0x20; // Arm OC5 TC5=TCNT+wait; TFLG1=0x20;} // clear C5F #pragma interrupt_handler KeyJhandler() void KeyJhandler(void){ KWIEH=KWIEJ=0x00; // all 16 are disarmed TMSK1|=0x20; // Arm OC5 TC5=TCNT+wait; TFLG1=0x20;} // clear C5F #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TMSK1&=0xDF; // Disarm OC5 KWIEH=KWIEJ=0xFF; // all 16 are rearmed KeyBoard();} // Read keys // Program 8.10. C software interface of a direct connection keyboard using periodic polling. // MC68HC812A4 unsigned int Key; // current pattern #define period 20000 // 10ms polling unsigned int KeyBoard(void){ return((PORTH<<8)+PORTJ);} // pattern void Ritual(void){ asm(" sei"); // make atomic TIOS|=0x20; // enable OC5 TSCR=0x90; // enable, fast clear TMSK2=0x32; // 500 ns clock TMSK1|=0x20; // Arm OC5 DDRH=DDRJ=0; // H,J are key inputs PUPEJ=0; // regular input Key=KeyBoard(); // read 16 keys TC5=TCNT+period; asm(" cli"); } #pragma interrupt_handler TOC5handler() void TOC5handler(void){ Key=KeyBoard(); // Current pattern TC5=TC5+period;} // ack OC5F // Program 8.12. C software interface of a matrix scanned keyboard. // MC68HC812A4 const struct Row { unsigned char out; unsigned char direction; unsigned char keycode[4];} #typedef const struct Row RowType; RowType ScanTab[5]={ { 0x70, 0x80, "abcd" }, // row 3 { 0xB0, 0x40, "efgh" }, // row 2 { 0xD0, 0x20, "ijkl" }, // row 1 { 0xE0, 0x10, "mnop" }, // row 0 { 0x00, 0x00, " " }}; void Ritual(void){ // PJ3-PJ0 are inputs DDRJ=0x00;} // PJ7-PJ4 are oc outputs /* Returns ASCII code for key pressed, Num is the number of keys pressed both equal zero if no key pressed */ unsigned char Scan(unsigned int *Num){ RowType *pt; unsigned char column,key; int j; (*Num)=0; key=0; // default values pt=&ScanTab[0]; while(pt->out){ PORTJ=pt->out; // select row DDRJ=pt->direction; // one output column=PORTJ; // read columns for(j=3; j>=0; j--){ if((column&0x01)==0){ key=pt->keycode[j]; (*Num)++;} column>>=1;} // shift into position pt++; } return key;} // Program 8.14. C software interface of a multiplexed keyboard. // MC68HC812A4 // PJ7-PJ4 row output // PJ3-PJ0 column inputs unsigned char Key; // current pattern unsigned char PreviousKey; // 10ms ago #define period 20000 // 10 ms unsigned char KeyScan(void){ unsigned char key,row; key=0; // means no key pressed for(row=0;row<16;row++){ PORTJ=row<<4; // Select row if((PORTJ&0x0F)!=0x0F){ key=PORTJ^0x0F; }} return(key);} void Ritual(void){ asm(" sei"); // make ritual atomic DDRJ=$F0; PreviousKey=Key=KeyScan(); // read TMSK1|=0x20; // Arm OC5 TIOS|=OC5; // enable OC5 TSCR|=0x80; // enable TMSK2=0x32; // 500 ns clock TC5=TCNT+wait; TFLG1=0x20; // clear OC5F asm(" cli"); } #pragma interrupt_handler TOC5handler() void TOC5handler(void){ unsigned char NewKey; NewKey=KeyScan(); // Current pattern if(NewKey==PreviousKey) Key=NewKey; PreviousKey=NewKey; TOC5=TOC5+period; TFLG1=0x20;} // ack OC5F // Program 8.15. C software interface of a scanned LED display. // MC68HC812A4 // PB7-PB0 output, 7 bit pattern // PC2-PC0 output, selects LED digit unsigned char code[3]; // binary codes static unsigned char select[3]={4,2,1}; unsigned int index; // 0,1,2 #define C5F 0x20 #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TFLG1=C5F; // Acknowledge TC5=TOC5+10000; // every 5 ms PORTC=select[index]; // which LED? PORTB=code[index]; // enable if(++index==3) index=0;} void ritual(void) { asm(" sei"); // make atomic index=0; DDRC=0xFF; // outputs 7 segment code DDRB=0xFF; // outputs select LED TMSK1|=C5F; // Arm OC5 TIOS|=C5F; // enable OC5 TSCR|=0x80; // enable TMSK2=0x32; // 500 ns clock TC5=TCNT+10000; asm(" cli"); } // Program 8.16. C software interface of a multiplexed LED display. // MC68HC812A4 unsigned int global; // 12 bit packed BCD const struct LED { unsigned char enable; // select unsigned char shift; // bits to shift const struct LED *Next; }; // Link #typedef const struct LED LEDType; #typedef LEDType * LEDPtr; LEDType LEDTab[3]={ { 0x04, 8, &LEDTab[1] }, // Most sig { 0x02, 4, &LEDTab[2] }, { 0x01, 0, &LEDTab[0] }}; // least sig LEDPtr Pt; // Points to current digit #define C5F 0x20 #pragma interrupt_handler TOC5handler() void TOC5handler(void){ TFLG1=C5F; // Acknowledge TC5=TC5+10000; // every 5 ms PORTB=(Pt->enable) +(global>>(pt->shift))<<4); Pt=Pt->Next; } void ritual(void) { asm(" sei"); // make atomic DDRB=0xFF; // outputs to LED's global=0; Pt=&LEDTab[0]; TMSK1|=C5F; // Arm OC5 TIOS|=C5F; // enable OC5 TSCR|=0x80; // enable TMSK2=0x32; // 500 ns clock TC5=TCNT+10000; asm(" cli"); } // Program 8.17. C software interface of an integrated LED display. // MC68HC812A4 // PS5/MOSI = MC14489 DATA IN // PS6/SCLK = MC14489 CLOCK IN // PS7 (simple output) = MC14489 ENABLE void ritual(void) { DDRS |= 0xE0; // outputs to MC14489 SP0CR1=0x50; // bit meaning // 7 SPIE=0 no interrupts // 6 SPE=1 SPI enable // 5 SWOM=0 regular outputs // 4 MSTR=1 master // 3 CPOL=0 match timing with MC14489 // 2 CPHA=0 // 1 SSOE=0 PS7 is simple output // 0 LSBF=0 MSB first SP0CR2=0x00; // no pull-up, regular drive SP0BR=0x02; // 1Mhz SCLK PORTS|= 0x80; // ENABLE=1 PORTS&= 0x7F; // ENABLE=0 SP0DR= 0x01; // hex format while(SP0SR&0x80)==0){}; PORTS|=0x80;} // ENABLE=1 void LEDout(unsigned char data[3]){ //packed PORTS &= 0x7F; // ENABLE=0 SP0DR = data[2]; // send MSbyte while(SP0SR&0x80)==0){}; SP0DR = data[1]; // send middle byte while(SP0SR&0x80)==0){}; SP0DR = data[0]; // send LSbyte while(SP0SR&0x80)==0){}; PORTS |= 0x80;} // ENABLE=1 // Program 8.18. Helper function for a simple LCD display. void LCDOutDigit(unsigned char position, unsigned char data) { // position is 0x80, 0x40, 0x20, or 0x10 and data is the BCD digit PORTB=0x0F&data; // set BCD digit on the A-D inputs of the MC14543B PORTB|=position; // toggle one of the LD inputs high PORTB=0x0F&data;} // LD=0, latch digit into MC14543B // Program 8.19. C software interface of a simple LCD display. void LCDOutNum(unsigned int data){ unsigned int digit,num,i; unsigned char pos; num=min(data,9999); // data should be unsigned from 0 to 9999 pos=0x10; // position of first digit (ones) for(i=0;i<4;i++){ digit=num%10; num=num/10; // next BCD digit 0 to 9 LCDOutDigit(pos,digit); pos=pos<<1;}} // Program 8.20. Bit-banged interface to a scanned LCD display. void LCDOut (unsigned char *pt) {unsigned int i; unsigned char mask; for(i=0;i<6;i++){ for(mask=0x80;mask;mask=mask>>1){ // look at bits 7,6,5,4,3,2,1,0 if((*pt)&mask) PORTB=1; else PORTB=0; // Serial data of the MC145000 PORTB|=2; // toggle the serial clock first high PORTB&=0xFD;} // then low pt++; }} // Program 8.21. SPI interface to a scanned LCD display using a MC145000. // MC68HC812A4 // PS5/MOSI = MC145000 DATA IN // PS6/SCLK = MC145000 CLOCK IN void ritual(void) { DDRS |= 0x60; // outputs to MC145000 SP0CR1=0x50; // bit meaning // 7 SPIE=0 no interrupts // 6 SPE=1 SPI enable // 5 SWOM=0 regular outputs // 4 MSTR=1 master // 3 CPOL=0 match timing with MC14489 // 2 CPHA=0 // 1 SSOE=0 PS7 is simple output // 0 LSBF=0 MSB first SP0CR2=0x00; // no pull-up, regular drive SP0BR=0x02;} // 1Mhz SCLK void LCDout(unsigned char data[6]){ unsigned int j; for(j=5; j>=0 ; j--){ SP0DR = data[j]; // Msbyte first while(SP0SR&0x80)==0){};}} // Program 8.22. Private functions for an HD44780 controlled LCD display. // MC68HC812A4 // 1 by 16 char LCD Display (HD44780) // ground = pin 1 Vss // power = pin 2 Vdd +5v // 10Kpot = pin 3 Vlc contrast adjust // PJ2 = pin 6 E enable // PJ1 = pin 5 R/W 1=read, 0=write // PJ0 = pin 4 RS 1=data, 0=control // PH0-7 = pins7-14 DB0-7 8 bit data #define LCDdata 1 // PJ0=RS=1 #define LCDcsr 0 // PJ0=RS=0 #define LCDread 2 // PJ1=R/W=1 #define LCDwrite 0 // PJ1=R/W=0 #define LCDenable 4 // PJ2=E=1 #define LCDdisable 0 // PJ2=E=0 void LCDcycwait(unsigned short cycles){ TC5=TCNT+cycles; // 500ns cycles to wait TFLG1 = 0x20; // clear C5F while((TFLG1&0x20)==0){};} // Program 8.23. Public functions for an HD44780 controlled LCD display. // MC68HC812A4 void LCDputchar(unsigned short letter){ // letter is ASCII code PORTH=letter; PORTJ=LCDdisable+LCDwrite+LCDdata; PORTJ=LCDenable+LCDwrite+LCDdata; // E=1 PORTJ=LCDdisable+LCDwrite+LCDdata; // E=0 LCDcycwait(80);} // 40 us wait void LCDputcsr(unsigned short command){ PORTH=command; PORTJ=LCDdisable+LCDwrite+LCDcsr; PORTJ=LCDenable+LCDwrite+LCDcsr; // E=1 PORTJ=LCDdisable+LCDwrite+LCDcsr; // E=0 LCDcycwait(80);} // 40 us wait void LCDclear(void){ LCDputcsr(0x01); // Clear Display LCDcycwait(3280); // 1.64ms wait LCDputcsr(0x02); // Cursor to home LCDcycwait(3280);} // 1.64ms wait void LCDinit(void){ DDRH=0xFF; DDRJ=0xFF; TIOS |= 0x20; // enable OC5 TSCR |= 0x80; // enable, no fast clear TMSK2=0xA2; // 500 ns clock LCDputcsr(0x06); // I/D=1 Increment, S=0 nodisplayshift LCDputcsr(0x0C); // D=1 displayon, // C=0 cursoroff, B=0 blinkoff LCDputcsr(0x14); // S/C=0 cursormove, R/L=0 shiftright LCDputcsr(0x30); // DL=1 8bit, N=0 1 line, F=0 5by7dots LCDclear(); } // clear display // Program 8.27. C linked list and helper functions used to control the stepper motor. const struct State { unsigned char Out; /* Output for this state */ const struct State *Next[2]; /* Next state CW or CCW motion */ }; #typedef struct State StateType; #typedef StateType * StatePtr; unsigned char POS; /* between 0 and 199 representing shaft angle */ #define clockwise 0 /* Next index*/ #define counterclockwise 1 /* Next index*/ StateType fsm[4]={ {10,{&fsm[1],&fsm[3]}}, { 9,{&fsm[2],&fsm[0]}}, { 5,{&fsm[3],&fsm[1]}}, { 6,{&fsm[0],&fsm[2]}} }; StatePtr Pt; /* Current State */ void CW(void){ Pt=Pt->Next[clockwise]; /* circulates around linked list */ PORTB=Pt->Out; /* step motor */ if(POS++==200) POS=0;} /* maintain shaft angle */ void CCW(void){ Pt=Pt->Next[counterclockwise]; /* circulates around linked list*/ PORTB=Pt->Out; /* step motor */ if(POS==0)POS=199; else POS--; } /* maintain shaft angle */ void Init(void){ DDRB=0xFF; // 6812 only POS=0; Pt=&fsm[0];} // Program 8.28. High-level C function to control the stepper motor. void SEEK(unsigned char New){ int CWsteps,i; if((CWsteps=New-POS)<0) CWsteps+=200; if(CWsteps>100) for(i=CWsteps;i<200;i++) CCW(); else for(i=0;i