| QUOTE |
Receiving Transaction: 757265 $0075.27 © clear table, (d) display table, (k) change key > Receiving Transaction: 957276 $7557.27 © clear table, (d) display table, (k) change key > Receiving Transaction: 727646 $0772.72 © clear table, (d) display table, (k) change key > d Account Amount 537757 $5768.31 757265 $0075.27 957276 $7557.27 727646 $0772.72 © clear table, (d) display table, (k) change key > k RSA key changed © clear table, (d) display table, (k) change key > Receiving Transaction: 054542 $0005.75 © clear table, (d) display table, (k) change key > c Data table cleared © clear table, (d) display table, (k) change key > d Account Amount © clear table, (d) display table, (k) change key > |
| QUOTE |
| Given a modulus m, a is a unit of m iff 0<a<m and GCD(a,m)=1. The number of units m has is F(m) (actually it's Phi(m), but that's not important here) If m is a prime, then F(m) = m-1. Also for any unit a of m, a^(F(m))=1 (mod m). Consequently, a^(k*F(m)+1)=a (mod m), for any k. This is the key behind RSA encryption. If you choose integers e and d, so that e*d =1 (mod (F(m)), then a^(e*d)=a(mod m). So to encrypt a number, RSA raises it to the e power, and to decrypt, it raises it again to the d power mod m. The Russian peasant arithmetic algorithm raises to large powers by squaring the result and reducing the exponent by half, and multiplying the result by the source when exponent is odd, everytime reducing mod m. because the number is reduced every iteration, there is no need to store a^d number anywhere and then reducing it mod m. instead, the biggest number stored is a^2, and it is reduced mod m. I wrote this algorithm in C, and then translated it to assembly manually. Here is the listing of the C code. Notice how the computational load is reduced by using this algorithm. |
| CODE |
include <stdio.h> int main(int argc, char *argv[]) { int result = 1; int s=atoi(argv[1]); //the number being encrypted int m=atoi(argv[2]); //the base int e=atoi(argv[3]); //the encoding key while(e>0) { if((e%2)==1){ result=(result*s)%m; e=e-1; } s=(s*s)%m; e=e/2; } printf("%d ",result); } |
| CODE |
;***** Final Project - Credit card transaction system with RSA encryption ******** ; ; Tony Cuadra, Ilyas Elkin ; ;************************************************ .nolist .include "c:\avrtools\appnotes\4414def.inc" .list .def char =r0 .def savSREG =r1;save the status register .def reload =r2;timer 0 interval .def lcdstat =r3 .def val0 =r4 .def val1 =r5 .def val2 =r6 .def val3 =r7 .def val4 =r8 .def val5 =r9 .def RSAe =r11 .def RSAd =r11;the encrypt method uses RSAd .def RSAm =r12 .def RSAs =r13 .def bitcnt =R16 ;bit counter .def temp =R17 ;temporary storage register .def Txbyte =R18 ;Data to be transmitted .def Rxbyte =R19 ;Received data .def mode =r20;mode of the machine (getting #=0001, getting$=0010, processing=0100) .def state =r21;state of the debouncer state machine .def time1 =r22;keeps track of time left for 30ms interrupt .def timeout =r23;Timeout value in mSec passed to subroutine .def butnum =r24;the button pressed on the keypad .def press =r25;the button detected previosly (used by debouncer) .def key =r26;encoding for the button pressed on the keypad .equ nopress =1;state when no button has been pressed .equ maybe =2;detected a button once so far .equ press =3;detected a button pressed at least twice in a row .equ bounce =4;detected a button release once .equ lcdrs =PA6;LCD rs pin connected to PD6 .equ lcdrw =PA5;LCD r/w pin connected to PD5 .equ lcde =PA4;LCD e pin connected to PD4 .equ key0 =0b01111101 .equ key1 =0b11101110 .equ key2 =0b11101101 .equ key3 =0b11101011 .equ key4 =0b11011110 .equ key5 =0b11011101 .equ key6 =0b11011011 .equ key7 =0b10111110 .equ key8 =0b10111101 .equ key9 =0b10111011 .equ keyA =0b11100111 .equ keyB =0b11010111 .equ keyILL =0xff;no button (or multiple buttons) pressed on keypad .equ getacct =0;bits for the mode .equ getcash =1 .equ process =2 .macro break cli rcall Monitor .endmacro ;******************************* ; Data segment .dseg acct: .byte 6;stores the account number cash: .byte 6;stores the transaction amount ;******************************* ; Initialization .cseg .org $0000 rjmp RESET;reset entry vector reti reti reti reti reti reti rjmp t0int reti rjmp Monitor reti rjmp Monitor reti RESET: ldi Temp, LOW(RAMEND);setup stack pointer out SPL, Temp ldi Temp, HIGH(RAMEND) out SPH, Temp ;PORTC is used for the keypad ;PORTA is used for the LCD ;setup timer 0 for overflow once every mSec ldi temp, 3 ;prescale timer by 64 out TCCR0, temp ldi temp,256-62;preload timer since mov reload, temp out TCNT0, Reload;62.5 x (64x.25) microSec = 1.0 mSec. ;turn on timer0 overflow interrupt ldi temp, exp2(TOIE0) out TIMSK, temp ;setup hardware UART -- enable RX, TX pins without ISRs ldi temp, 0b00011000 out UCR, temp ;set baud rate to 9600 ldi temp, 25 out UBRR, temp ;enable receive ISR ONLY for keyboard monitor sbi UCR, RXCIE ldi mode, EXP2(getacct) ;setup software UART cbi PORTD,RxD cbi DDRD,RxD sbi PORTD,TxD sbi DDRD,TxD ;initial conditions ldi time1, 30;set time1 for 30 mSec ldi state, nopress;set initial state to nopress clr val0 clr val1 clr val2 clr val3 clr val4 clr val5 sei ldi temp, 0xff out DDRA, temp out PORTA, temp rcall lcdinit ;Initialize LCD module rcall lcdclr ;Clear LCD screen rcall getinput ;*************************** Sched: tst time1 ;-*test*-('") for first task ready brne Sched ;if not then loop rcall debounce tskend: rjmp Sched ;*************************** debounce: ldi time1, 30;reset for another 30 mSec cpi state, nopress breq _nopress cpi state, maybe breq _maybe cpi state, press breq _press cpi state, bounce breq _bounce error: rjmp error _nopress: rcall keybd cpi butnum, keyILL;check if nothing is pressed breq _nopressY mov press, butnum ldi state, maybe _nopressY: ret _maybe: rcall keybd cp press, butnum breq _maybeY ldi state, nopress ret _maybeY:ldi state, press rcall gotkey ret _press: rcall keybd cp press, butnum breq _pressY ldi state, bounce _pressY:ret _bounce:rcall keybd cp press, butnum breq _bounceY ldi state, nopress ret _bounceY: ldi state, press ret ;*************************** getinput: ldi temp, '0' mov val0, temp mov val1, temp mov val2, temp mov val3, temp mov val4, temp mov val5, temp rcall display ret ;*************************** gotkey: sbrc mode, process ret cpi press, 'A';-*test*-('") for go command breq Mstart ;done with this input mov val5,val4;shift all the frequency digits left mov val4,val3 mov val3,val2 mov val2,val1 mov val1,val0 mov val0,press;shift in the newly read digit display: rcall lcdclr sbrc mode, getacct ldi temp, '#' sbrc mode, getcash ldi temp, '$' rcall lcdput mov temp, val5 rcall lcdput mov temp, val4 rcall lcdput mov temp, val3 rcall lcdput mov temp, val2 rcall lcdput ldi temp, '.' sbrc mode, getcash rcall lcdput mov temp, val1 rcall lcdput mov temp, val0 rcall lcdput ret ;*************************** Mstart: cpi mode, EXP2(getacct) breq stacct ldi ZL, LOW(cash) ldi ZH, HIGH(cash) rjmp stcash stacct: ldi ZL, LOW(acct) ldi ZH, HIGH(acct) stcash: st Z, val5 std Z+1, val4 std Z+2, val3 std Z+3, val2 std Z+4, val1 std Z+5, val0 cpi mode, EXP2(getcash) breq proc ldi mode, EXP2(getcash) rcall getinput ret proc: ldi mode, EXP2(process) ldi timeout, 10 rcall delay ;delay 10 ms ;************************** cli;disable interrupts so we can receive the key rcall putchar rcall getchar mov RSAm, Rxbyte rcall getchar mov RSAe, Rxbyte sei;enable interrupts after receiving key ;************************** ldi ZL, LOW(acct) ldi ZH, HIGH(acct) encrloop: ld RSAs, Z ldi temp, '0' sub RSAs, temp rcall encrypt ldi timeout, 20 rcall delay ;delay 20 ms mov Txbyte, RSAs rcall putchar ldi timeout, 1 rcall delay ;delay 1 ms adiw ZL,1 cpi ZL, LOW(acct+12) brne encrloop ldi mode, EXP2(getacct) rcall getinput ret keypressed: ret;ignore keys received on the hardware UART ;*************************** keybd: ldi temp, 0x0f;set lower four lines to output out DDRC, temp ldi temp, 0xf0;and turn on the pullups on the inputs out PORTC, temp nop ;Need some time for the pullups to nop ;charge the port pins nop nop in temp, PINC;read the high nibble mov key, temp;and store it (with zeros in the low nibble) ldi temp, 0xf0;set upper four lines to outputs out DDRC, temp ldi temp, 0x0f;and turn on pullups on the inputs out PORTC, temp nop ;As before wait for the pin to charge nop nop nop in temp, PINC;read the low nibble or key, temp;combine to make key code ;At the point the raw key code should have exactly one zero each in ;the lower and upper nibbles. Any other number of zeros indicates ;either no-button pressed or multiple-button pressed. ldi butnum, '0' cpi key, key0 breq tbldone ldi butnum, '1' cpi key, key1 breq tbldone ldi butnum, '2' cpi key, key2 breq tbldone ldi butnum, '3' cpi key, key3 breq tbldone ldi butnum, '4' cpi key, key4 breq tbldone ldi butnum, '5' cpi key, key5 breq tbldone ldi butnum, '6' cpi key, key6 breq tbldone ldi butnum, '7' cpi key, key7 breq tbldone ldi butnum, '8' cpi key, key8 breq tbldone ldi butnum, '9' cpi key, key9 breq tbldone ldi butnum, 'A' cpi key, keyA breq tbldone ldi butnum, keyILL tbldone:ret ;***************************** ;interrupt routines ;***************************** ;timer 0 ISR (timer-zero overflow) ;Enters every 1.0 mSec t0int: in savSREG, SREG out TCNT0, reload; keeps clock ticking at 1 mSec dec timeout ;subtract another mSec tst time1 breq t0exit dec time1 t0exit: out SREG, savSREG reti ;back to backgound tasks .include ".\lcd.inc" .include ".\keymon3.inc" .include ".\uart.inc" .include ".\rsa.inc" |
| CODE |
;***** Final Project - Credit card transaction system with RSA encryption ******** ; ; Tony Cuadra, Ilyas Elkin ; ;************************************************ .nolist .include "c:\avrtools\appnotes\4414def.inc" .list .def char =r0 .def savSREG =r1;save the status register .def RSAd =r11 .def RSAm =r12 .def RSAs =r13 .def RSAe =r14 .def bitcnt =R16 ;bit counter .def temp =R17 ;temporary storage register .def Txbyte =R18 ;Data to be transmitted .def Rxbyte =R19 ;Received data .def mode =r20;mode of the machine (getting #=0001, getting$=0010, processing=0100) .def temp =r21;temporary register .def timeout =r22;Timeout value in mSec passed to subroutine .def keyindex=r23;index for the key table .def tblindex=r24;index for the data table .def count =r25;count how many entries we have received .def TXflash =r26;text to be sent is in flash if <>0 .def first =r27;flag that is set to 1 if we are receiving the account num .def RXchar =r28;char received by the hardware UART .equ keymax =3*3 .equ tblmax =15*10 ;******************************* ; Data segment .dseg table: .byte 15*10;enough room for 10 entries ;6 byte account num + 0-term + 6 byte transaction amt + 0-term ;************************************** ;print and read string macros .macro PrintFlashStr;use: PrintFlashStr flashaddress ldi TXflash, 1 ldi ZL, low(@0<<1) ldi ZH, high(@0<<1) rcall putString .endmacro .macro PrintRamStr;use: PrintRamStr ramaddress ldi TXflash, 0 ldi ZL, low(@0) ldi ZH, high(@0) rcall putString .endmacro .macro PrintRamStr2;use: PrintRamStr2 ramaddress, offset_register ldi TXflash, 0 ldi ZL, low(@0) ldi ZH, high(@0) add ZL, @1 adc ZH, TXflash rcall putString .endmacro .macro LED ;use: LED register_to_output push temp mov temp, @0 com temp out PORTB, temp pop temp .endmacro .macro LED2 ;use: LED constant push temp ldi temp, @0 com temp out PORTB, temp pop temp .endmacro .macro delaymac;use: delaymac time (mSec) ldi timeout, @0 rcall delay2 .endmacro ;******************************* ; Initialization .cseg .org $0000 rjmp RESET;reset entry vector reti reti reti reti reti reti reti reti rjmp Monitor reti rjmp Monitor reti ;define fixed strings to be transmitted from flash- zero terminated prompt: .db "(c) clear table, (d) display table, (k) change key > ",0x00 heading:.db "Account Amount",0x00 spacer: .db " $",0x00 newline:.db 0x0d, 0x0a, 0x00;carrage return/line feed Receiving: .db "Receiving Transaction: ", 0x00 TableCleared: .db "Data table cleared", 0x00 KeyChanged: .db "RSA key changed", 0x00 ;define RSA keys: keys: .db 241, 83, 107, 251, 127, 63, 221, 23, 167 RESET: ldi Temp, LOW(RAMEND);setup stack pointer out SPL, Temp ldi Temp, HIGH(RAMEND) out SPH, Temp ;setup UART -- enable RX, TX pins without ISRs ldi temp, 0b00011000 out UCR, temp ;set baud rate to 9600 ldi temp, 25 out UBRR, temp ;enable receive ISR ONLY for keyboard monitor sbi UCR, RXCIE ;set up the PORTs ser temp ;set PORTB to be out DDRB,temp;all outputs (for LEDs) ;software UART cbi PORTD,RxD cbi DDRD,RxD sbi PORTD,TxD sbi DDRD,TxD ;initial conditions clr tblindex clr keyindex rcall newkey printFlashStr newline printFlashStr prompt sei spin: rcall getchar cli;disable interrupts so we can receive data cpi tblindex, tblmax brne notfull printFlashStr newline printFlashStr newline rcall tblflush;output the table contents to the terminal clr tblindex;clear the table notfull: printFlashStr newline printFlashStr newline printFlashStr Receiving delaymac 10 mov Txbyte, RSAm rcall putchar delaymac 10 mov Txbyte, RSAe rcall putchar ser first;set first when we are receiving the account num ldi count, 6 ldi ZL, LOW(table) ldi ZH, HIGH(table) clr temp add ZL, tblindex adc ZH, temp getentry: rcall getchar mov RSAs, Rxbyte rcall encrypt ldi temp, '0' add RSAs, temp st Z, RSAs mov char, RSAs rcall putc adiw ZL, 1 cpi count, 3 breq decimal get_1: dec count brne getentry ldi count, 6 clr temp st Z, temp;write terminating 0 adiw ZL, 1 tst first ;if first time through (got account num) breq notfirst clr first push ZL push ZH printFlashStr spacer pop ZH pop ZL rjmp getentry;go through again to get transaction amount notfirst: subi tblindex, -15 ;point to next entry in the table printFlashStr newline printFlashStr newline printFlashStr prompt sei;enable interrupts (done receiving data) rjmp spin decimal: tst first brne get_1 ldi temp, '.' mov char, temp rcall putc st Z, char adiw ZL, 1 rjmp get_1 ;*************************** ;output the entire contents of the table to the terminal tblflush: printFlashStr heading printFlashStr newline clr count tblflush_1: cp count, tblindex breq tblflush_2 printRamStr2 table, count subi count, -7 printFlashStr spacer printRamStr2 table, count subi count, -8 printFlashStr newline rjmp tblflush_1 tblflush_2: printFlashStr newline ret ;*************************** ;change RSA key newkey: clr temp ldi ZL, LOW(keys*2) ldi ZH, HIGH(keys*2) add ZL, keyindex adc ZH, temp lpm mov RSAm, r0 adiw ZL, 1 lpm mov RSAe, r0 adiw ZL, 1 lpm mov RSAd, r0 subi keyindex, -3 cpi keyindex, keymax brne newkey_1 clr keyindex newkey_1: ret ;*************************** ;handle input from hardware UART keypressed: cpi RXchar, 'd' breq key_disp cpi RXchar, 'D' breq key_disp cpi RXchar, 'c' breq key_clr cpi RXchar, 'C' breq key_clr cpi RXchar, 'k' breq key_newkey cpi RXchar, 'K' breq key_newkey ret key_disp: mov char, RXchar rcall putc printFlashStr newline printFlashStr newline rcall tblflush printFlashStr prompt ret key_clr: mov char, RXchar rcall putc clr tblindex printFlashStr newline printFlashStr newline printFlashStr TableCleared printFlashStr newline printFlashStr newline printFlashStr prompt ret key_newkey: mov char, RXchar rcall putc rcall newkey printFlashStr newline printFlashStr newline printFlashStr KeyChanged printFlashStr newline printFlashStr newline printFlashStr prompt ret ;********************************************* ;string soubroutines ;********************************************* ;routine to output one char ;enter with char in char putc: sbis USR, UDRE;wait until clear then send one char rjmp putC out UDR, char ret ;routine to input one char ;exit with char in char getc: sbis USR, RXC;wait until ready then get one char rjmp getc in char, UDR ret ;routine to send a string putString: tst TXflash ;is it flash? breq _txram lpm;char ;if so get a char rjmp _tstend _txram: ld char, Z ;if mem get a char _tstend:tst char ;is it string end marker? breq _TXend rcall putc ;emit a char adiw ZL, 1 ;set up next pointer rjmp putString _TXend: ret delay2: push temp delay2_1: ldi temp, 250 delay2_2: nop nop nop nop nop nop nop nop nop nop nop nop nop dec temp brne delay2_2 dec timeout brne delay2_1 pop temp ret .include ".\uart.inc" .include ".\keymon3.inc" .include ".\rsa.inc" |
| CODE |
;*******************RSA encryption Routine************ ;**Encrypts Numbers using 8 bit RSA encryption******** ;def statements: ;.def RSAd =r21 ;.def RSAm =r22 ;.def RSAs =r13 .def RSAr =r24 .def drem16uL=r14 .def drem16uH=r15 .def dres16uL=r16 .def dres16uH=r17 .def dd16uL =r16 .def dd16uH =r17 .def dv16uL =r18 .def dv16uH =r19 .def dcnt16u =r20 ;changed these around a little .def mc8u =r18 ;multiplicand .def mp8u =r16 ;multiplier .def m8uL =r16 ;result Low byte .def m8uH =r17 ;result High byte .def mcnt8u =r19 ;loop counter ;***********end def statements*********** encrypt: push RSAr;push all the used registers except the source push RSAd push RSAm push r14 push r15 push r16 push r17 push r18 push r19 push r20 ldi RSAr, 1 ; result=1 encloop: sbrs RSAd, 0;if(e%2=1) do, else skip rjmp endencif mov mp8u, RSAr;multiply result by source mov mc8u, RSAs;source is s rcall mpy8u ;mov dd16uH, m8uH;divide the product from above ;mov dd16uL, m8uL mov dv16uL, RSAm;by the modulus clr dv16uH; 16/8 divide rcall div16u mov RSAr, drem16uL; result is the remainder endencif: mov mp8u, RSAs mov mc8u, RSAs; source is s rcall mpy8u ;mov dd16uH, m8uH ;mov dd16uL, m8uL mov dv16uL, RSAm clr dv16uH; 16/8 divide rcall div16u mov RSAs, drem16uL; s is the remainder lsr RSAd; integer divide e by 2 clr r14 cp RSAd, r14 brne encloop endencloop: mov RSAs,RSAr;store the result in the register where source is. pop r20 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 pop RSAm pop RSAd pop RSAr ret ;*************************************************************************** ;* ;* "mpy8u" - 8x8 Bit Unsigned Multiplication ;* ;* This subroutine multiplies the two register variables mp8u and mc8u. ;* The result is placed in registers m8uH, m8uL ;* ;* Number of words :9 + return ;* Number of cycles :58 + return ;* Low registers used :None ;* High registers used :4 (mp8u,mc8u/m8uL,m8uH,mcnt8u) ;* ;* Note: Result Low byte and the multiplier share the same register. ;* This causes the multiplier to be overwritten by the result. ;* ;*************************************************************************** ;***** Subroutine Register Variables ;***** Code mpy8u: clr m8uH ;clear result High byte ldi mcnt8u,8;init loop counter lsr mp8u ;rotate multiplier m8u_1: brcc m8u_2 ;carry set add m8uH,mc8u; add multiplicand to result High byte m8u_2: ror m8uH ;rotate right result High byte ror m8uL ;rotate right result L byte and multiplier dec mcnt8u ;decrement loop counter brne m8u_1 ;if not done, loop more ret ;*************************************************************************** ;* ;* "div16u" - 16/16 Bit Unsigned Division ;* ;* This subroutine divides the two 16-bit numbers ;* "dd8uH:dd8uL" (dividend) and "dv16uH:dv16uL" (divisor). ;* The result is placed in "dres16uH:dres16uL" and the remainder in ;* "drem16uH:drem16uL". ;* ;* Number of words :19 ;* Number of cycles :235/251 (Min/Max) ;* Low registers used :2 (drem16uL,drem16uH) ;* High registers used :5 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH, ;* dcnt16u) ;* ;*************************************************************************** ;***** Subroutine Register Variables ;***** Code div16u: clr drem16uL;clear remainder Low byte sub drem16uH,drem16uH;clear remainder High byte and carry ldi dcnt16u,17;init loop counter d16u_1: rol dd16uL ;shift left dividend rol dd16uH dec dcnt16u ;decrement counter brne d16u_2 ;if done ret ; return d16u_2: rol drem16uL;shift dividend into remainder rol drem16uH sub drem16uL,dv16uL;remainder = remainder - divisor sbc drem16uH,dv16uH; brcc d16u_3 ;if result negative add drem16uL,dv16uL; restore remainder adc drem16uH,dv16uH clc ; clear carry to be shifted into result rjmp d16u_1 ;else d16u_3: sec ; set carry to be shifted into result rjmp d16u_1 |
| CODE |
;LCD include file ;================================================ ; Clear entire LCD and delay for a bit lcdclr: ldi temp,1 ;Clear LCD command rcall lcdcmd ldi timeout,3;Delay 3 mS for clear command rcall delay ret ;================================================ ; Initialize LCD module lcdinit: ldi temp,0x80;Setup port pins out PORTA,temp;Pull all pins low (except D7) ldi temp,0xff;All pins are outputs out DDRA,temp ldi timeout,15;Wait at least 15 mS at power up rcall delay ; LCD specs call for 3 repetitions as follows: ;first rep ldi temp,0x83;Function set out PORTA,temp;to 8-bit mode nop ;nop is data setup time sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ldi timeout,15;Wait at least 15 mS rcall delay ;second rep ldi temp,0x83;Function set out PORTA,temp nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ldi timeout,15;Wait at least 15 ms rcall delay ;third rep ldi temp,0x83;Function set out PORTA,temp nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ldi timeout,15;Wait at least 15 ms rcall delay ;Now change to 4-wire interface mode ldi temp,0x82;Function set, 4 wire databus out PORTA,temp nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ; Finally, at this point, ; the normal 4 wire command routine (lcdcmd) can be used ldi temp,0b00100000;Function set, 4 wire, 1 line, 5x7 font rcall lcdcmd ldi temp,0b00001100;Display on, no cursor, no blink rcall lcdcmd ldi temp,0b00000110;Address increment, no scrolling rcall lcdcmd ret ;============================================ ; Wait for LCD to go unbusy lcdwait: ldi temp,0xF0;Make 4 data lines inputs out DDRA,temp sbi PORTA,lcdrw;Set r/w pin to read cbi PORTA,lcdrs;Set register select to command waitloop: sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde in lcdstat,PINA;Read busy flag ;Read, and ignore lower nibble sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde sbrc lcdstat,3;Loop until done rjmp waitloop ret ;============================================= ; Send command in temp to LCD lcdcmd: push temp ;Save character rcall lcdwait ldi temp,0xFF;Make all port D pins outputs out DDRA,temp pop temp ;Get character back push temp ;Save another copy swap temp ;Get upper nibble andi temp,0x0F;Strip off upper bits ori temp,0x80 out PORTA,temp;Put on port nop ;wait for data setup time sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde pop temp ;Recall character andi temp,0x0F;Strip off upper bits ori temp,0x80 out PORTA,temp;Put on port nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ret ;============================================= ; Send character data in temp to LCD lcdput: push temp ;Save character rcall lcdwait ldi temp,0xFF;Make all port D pins outputs out DDRA,temp pop temp ;Get character back push temp ;Save another copy swap temp ;Get upper nibble andi temp,0x0F;Strip off upper bits ori temp,0x80 out PORTA,temp;Put on port sbi PORTA,lcdrs;Register select set for data nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde pop temp ;Recall character andi temp,0x0F;Strip off upper bits ori temp,0x80 out PORTA,temp;Put on port sbi PORTA,lcdrs;Register select set for data nop sbi PORTA,lcde;Toggle enable line nop cbi PORTA,lcde ret ;============================================= ; subroutine waits for time equal to value in register timeout ;the register 'timeout' should be loaded before the call delay: tst timeout brne delay ret ;============================================= |
| CODE |
;;******************************* ;;***** RS-232 keyboard monitor ;;******************************* ;To be included into a program under -*test*-('"). ;************************************ ; keyboard monitor and optional receive ISR ;returns a user char UNLESS the char is "esc" or entry was via rcall .def RXtemp=r29;a temp register .def RXparm=r28;subroutine parameter .def RXsave=r27;save the SREG .equ npush=5 ;the number of saved registers in RXisr Monitor: ;save processor status push ZH push ZL push RXtemp push RXparm push RXsave in RXsave, SREG sbis USR, RXC;is a char available rjmp RXcmd ;if not go to cmd handler in RXparm, UDR;get the character cpi RXparm, 0x1b;is the char an "esc" breq RXcmd ;if so go to cmd handler ;###### ;Uncomment the next MOV if your program expects to have the receive-done ;ISR return a character. You can also put other code here, perhaps ;a pointer increment or <cr> detection. rcall keypressed ;###### RXexit: out SREG, RXsave;restore proc status pop RXsave pop RXparm ;restore program state pop RXtemp pop ZL pop ZH reti ;back to pgm ; ;************************************** ; monitor command interperter RXcmd: ldi RXparm, 0x0d;emit "cr" for a prompt rcall RXputc ldi RXparm, 0x0a;emit "lf" for a prompt rcall RXputc ldi RXparm, '>';emit ">" for a prompt rcall RXputc ;wait for a command char rcall RXgetc ;is it a go command? RXgo: cpi RXparm, 'g';if so return to user code brne RXreg rcall RXputc ;echo the 'g' cbi UCR, TXCIE;clear single-step isr sbi USR, TXC;and its flag breq RXexit ;is it a reg read command? RXreg: cpi RXparm, 'r';dump a register brne RXregW rcall RXputc ;echo the 'r' rcall RXget2digit ;find out which reg cpi RXparm, 32-npush;is the reg stored on the stack brge _stkreg mov ZL, RXparm ;if not, just read it back clr ZH ld RXparm, Z ;remember that 0-1f is reg rcall RXput2char rjmp RXcmd _stkreg:in ZL, SPL ;if the reg is stacked compute the address in ZH, SPH ;on the stack subi RXparm, 32-npush-1;regs were stacked in numerical order add ZL, RXparm ;stack offset clr RXparm adc ZH, RXparm ld RXparm, Z rcall RXput2char rjmp RXcmd ;is is a register write command? RXregW: cpi RXparm, 'R' ;write a register brne RXmem rcall RXputc ;echo the 'R' rcall RXget2digit ;find out which reg mov ZL, RXparm ;store register number in ZL ldi RXparm, '=' ;prompt with a equal sign rcall RXputc rcall RXget2digit ;get the new reg value cpi ZL, 32-npush ;is the reg stored on the stack? brge _stkrgw ;if not, just write it clr ZH ;ZL already has reg number st Z, RXparm ;remember that 0-1f is reg rjmp RXcmd _stkrgw:mov RXtemp, RXparm ;RXtemp has new reg value mov RXparm, ZL ;RXparm now has reg number in ZL, SPL ;if the reg is stacked compute the address in ZH, SPH ;on the stack subi RXparm, 32-npush-1;regs were stacked in numerical order add ZL, RXparm ;stack offset clr RXparm adc ZH, RXparm st Z, RXtemp rjmp RXcmd ;memory read RXmem: cpi RXparm, 'm' brne RXmemw rcall RXputc ;echo rcall RXget2digit;get a 4 digit address mov ZH, RXparm rcall RXget2digit mov ZL, RXparm ld RXparm, Z;load mem data rcall RXput2char rjmp RXcmd ;memory write RXmemw: cpi RXparm, 'M' brne RXio rcall RXputc ;echo rcall RXget2digit;get a 4 digit address mov ZH, RXparm rcall RXget2digit mov ZL, RXparm ldi RXparm, '=' ;prompt with a equal sign rcall RXputc rcall RXget2digit ;get the new memory value st Z, RXparm ;now store it rjmp RXcmd ;i/o register read RXio: cpi RXparm, 'i' brne RXiow rcall RXputc ;echo rcall RXget2digit subi RXparm, -0x20;i/o register offset mov ZL, RXparm;pointer to i/o reg clr ZH ld RXparm, Z;now get it rcall RXput2char rjmp RXcmd ;i/o register write RXiow: cpi RXparm, 'I' brne RXsreg rcall RXputc ;echo rcall RXget2digit subi RXparm, -0x20;i/o register offset mov ZL, RXparm;pointer to i/o reg clr ZH ldi RXparm, '=' ;prompt with a equal sign rcall RXputc rcall RXget2digit ;get the new reg value st Z, RXparm;now store it rjmp RXcmd ;get SREG RXsreg: cpi RXparm, 's' brne RXpc rcall RXputc ;echo mov Rxparm, RXsave;get the saved SREG rcall RXput2char rjmp RXcmd ;get the program counter RXpc: cpi RXparm, 'p' brne RXpcw rcall RXputc ;echo in ZL, SPL ;get prog counter from the stack in ZH, SPH ldd RXparm, Z+npush+1;stack offset to high byte of PC rcall RXput2char ldd RXparm, Z+npush+2;stack offset to low byte of PC rcall RXput2char2 rjmp RXcmd ;write the program counter RXpcw: cpi RXparm, 'P' brne RXstep rcall RXputc ;echo the P ldi RXparm, '=';prompt with a equal sign rcall RXputc rcall RXget2digit;high order PC in ZL, SPL ;get prog counter from the stack in ZH, SPH std Z+npush+1, RXparm;stack offset to high byte of PC rcall RXget2digit std Z+npush+2, RXparm;stack offset to low byte of PC rjmp RXcmd RXstep: cpi RXparm, 't' brne RXz rcall RXputc ;echo in ZL, SPL ;get prog counter from the stack in ZH, SPH ldd RXparm, Z+npush+1;stack offset to high byte of PC rcall RXput2char ldd RXparm, Z+npush+2;stack offset to low byte of PC rcall RXput2char2 ldi RXparm, ' ' ;ascii space rcall RXputc sbi UCR, TXCIE ;kick-start TX complete interrupt _twait: sbis USR, TXC ;and wait for the TX complete rjmp _twait ;bit to be true rjmp RXexit RXz: cpi RXparm, 'z' brne RXhuh rcall RXputc ;echo z in ZL, SPL ;get z-register from the stack in ZH, SPH ldd RXparm, Z+npush ;stack offset to ZH rcall RXput2char ldd RXparm, Z+npush-1;stack offset to ZL rcall RXput2char2 ldd RXparm, Z+npush ;now get Z again ldd RXtemp, Z+npush-1 mov ZH, RXparm mov ZL, RXtemp ;push r0 ;lpm ;mov RXparm, r0 ;pop r0 ;rcall RXput2char ld RXparm, Z;print the memory pointed to by z rcall RXput2char rjmp RXcmd ;unknown command trap RXhuh: ldi RXparm, '?' rcall RXputc rjmp RXcmd ; ;********************************************* ;utility routine to output one char ;enter with char in RXparm RXputc: sbis USR, UDRE;wait until clear then send one char rjmp RXputC out UDR, RXparm ret ;utility routine to input one char ;exit with char in RXparm RXgetc: sbis USR, RXC;wait until ready then get one char rjmp RXgetc in RXparm, UDR ret ;utility routine to make a ascii char into a hex value ;enter with char in RXparm RXcharTohex: cpi RXparm, 'a' brge _aboveA cpi RXparm, '0';error check-- is digit <'0'? brge _n1 rjmp RXcmd _n1: cpi RXparm, ':';error check -- is digit <':' brlt _n2 rjmp RXcmd ;if here then digit is 0 to 9 _n2: subi RXparm, '0' ret _aboveA: cpi RXparm, 'g';error check -- is digit>'f' brlt _ab1 rjmp RXcmd _ab1: subi RXparm, 'a'-10 ret ;utility routine to input rwo ascii chars and ; convert into a hex value returned in RXparm RXget2digit: rcall RXgetc ;get the highorder reg # nibble rcall RXputc ;echo it rcall RXcharTohex;convert to hex swap RXparm ;make it the highorder bits mov RXtemp, RXparm;and save it rcall RXgetc ;get the loworder reg # nibble rcall RXputc rcall RXcharTohex or RXparm, RXtemp;combine high and low nibbles ret ;utility routine to output two ascii chars given a ;hex value in RXparm RXput2char: mov RXtemp, RXparm;a copy ldi RXparm, '=';a delimiter rcall RXputc ;print the delimiter mov RXparm, RXtemp;restore input RXput2char2:;entry used only for the second byte of PC dump mov RXtemp, RXparm andi RXparm, 0xf0;mask high digits swap RXparm ;and make them loworder cpi RXparm, 10;numbers or letters? brge _f8f1 subi RXparm, -'0';convert a numeric digit to ascii rjmp _f8tx1 _f8f1: subi RXparm, -'a'+10;convert a letter to ascii _f8tx1: rcall RXputc mov RXparm, RXtemp;now repeat for the other digit andi RXparm, 0x0f cpi RXparm, 10 brge _f8f2 subi RXparm, -'0' rjmp _f8tx2 _f8f2: subi RXparm, -'a'+10 _f8tx2: rcall RXputc ret ;*********************************** |
| CODE |
;**** A P P L I C A T I O N N O T E A V R 3 0 5 ************************ ;* ;* Title : Half Duplex Interrupt Driven Software UART ;* Version : 1.1 ;* Last updated : 97.08.27 ;* Target : AT90Sxxxx (All AVR Device) ;* ;* Support email : avr@atmel.com ;* ;* Code Size : 32 Words ;* Low Register Usage : 0 ;* High Register Usage : 4 ;* Interrupt Usage : None ;* ;* DESCRIPTION ;* This Application note contains a very code efficient software UART. ;* The example program receives one character and echoes it back. ;*************************************************************************** ;***** Pin definitions .equ RxD =6 ;Receive pin is PD6 .equ TxD =7 ;Transmit pin is PD7 ;***** Global register variables .def bitcnt =R16 ;bit counter .def temp =R17 ;temporary storage register .def Txbyte =R18 ;Data to be transmitted .def RXbyte =R19 ;Received data .cseg ;*************************************************************************** ;* ;* "putchar" ;* ;* This subroutine transmits the byte stored in the "Txbyte" register ;* The number of stop bits used is set with the sb constant ;* ;* Number of words :14 including return ;* Number of cycles :Depens on bit rate ;* Low registers used :None ;* High registers used :2 (bitcnt,Txbyte) ;* Pointers used :None ;* ;*************************************************************************** .equ sb =1 ;Number of stop bits (1, 2, ...) putchar: ldi bitcnt,9+sb;1+8+sb (sb is # of stop bits) com Txbyte ;Inverte everything sec ;Start bit putchar0: brcc putchar1;If carry set cbi PORTD,TxD; send a '0' rjmp putchar2;else putchar1: sbi PORTD,TxD; send a '1' nop putchar2: rcall UART_delay;One bit delay rcall UART_delay lsr Txbyte ;Get next bit dec bitcnt ;If not all bit sent brne putchar0; send next ;else ret ; return ;*************************************************************************** ;* ;* "getchar" ;* ;* This subroutine receives one byte and returns it in the "Rxbyte" register ;* ;* Number of words :14 including return ;* Number of cycles :Depens on when data arrives ;* Low registers used :None ;* High registers used :2 (bitcnt,Rxbyte) ;* Pointers used :None ;* ;*************************************************************************** getchar: ldi bitcnt,9;8 data bit + 1 stop bit getchar1: sbic PIND,RxD;Wait for start bit rjmp getchar1 rcall UART_delay;0.5 bit delay getchar2: rcall UART_delay;1 bit delay rcall UART_delay clc ;clear carry sbic PIND,RxD;if RX pin high sec ; dec bitcnt ;If bit is stop bit breq getchar3; return ;else ror Rxbyte ; shift bit into Rxbyte rjmp getchar2; go get next getchar3: ret ;*************************************************************************** ;* ;* "UART_delay" ;* ;* This delay subroutine generates the required delay between the bits when ;* transmitting and receiving bytes. The total execution time is set by the ;* constant "b": ;* ;* 3·b + 7 cycles (including rcall and ret) ;* ;* Number of words :4 including return ;* Low registers used :None ;* High registers used :1 (temp) ;* Pointers used :None ;* ;*************************************************************************** ; Some b values: (See also table in Appnote documentation) ; ; 1 MHz crystal: ; 9600 bps - b=14 ; 19200 bps - b=5 ; 28800 bps - b=2 ; ; 2 MHz crystal: ; 19200 bps - b=14 ; 28800 bps - b=8 ; 57600 bps - b=2 ; 4 MHz crystal: ; 19200 bps - b=31 ; 28800 bps - b=19 ; 57600 bps - b=8 ; 115200 bps - b=2 .equ b =250 ;.equ b =31;19200 bps @ 4 MHz crystal UART_delay: ldi temp,b UART_delay1: dec temp brne UART_delay1 ret |