|
Below is some assembly code I wrote for the mega128 last fall. It worked but has not been tested extensively. I did a look-up
table to give me a ASCII character for the corresponding keyboard scan code.
; Date: 9/26/03
; By: Stephen L. Nuttall
; Hardware platform; STK500/STK501
; Software platform: Atmel Studio 4.07
; File location: C:\My Documents\AVR Projects\keyboard4a\keyboard4a.asm
; Clock frequency: 3.6864MHz (via STK500 onboard oscillator)
;
;
; The purpose of this project is to:
; 1. Read a keyboard scan code using the keyboard SCL and SDA lines
; 2. Determine if the scan code is 0xAA in which case it will transmit
; "Basic Assurance Test complete" via USART0
; 3. Determine if the scan code is 0xF0, if it is not it will look up and
; transmit the equivalent ASCII code for that key (in other words,
; if the key is held down it will keep transmitting that ASCII character).
; If it is 0xF0 then it will transmit the word "Break" and then wait for
; the next scan code
; 4. Only the alphabet and number keys are decoded in this code. The Ctrl, Alt,
; Shift, Enter, Function, etc. keys are not decoded. Hitting one of those keys
; will result in a capital letter or a "?" being sent via the USART0. You can
; look at the table at the end of the code and get an idea of what I mean.
;
; This project will use code from previous projects as well as code
; from keyboard5.asm, keyboard5a.asm and keyboard5b.asm.
;
.nolist
.include "m128def.inc"
.list
;
.def temp = R16
.def bitcntr = R17
.def keydata = R18
.def temphi = R19
.def templo = R20
.def flags = R21
.def parity = R22
;
.org 0x0000
jmp RESET ; Reset Handler
.org 0x0046
RESET: ; Main program start
ldi temp, high(RAMEND) ; Stack pointer = top of internal SRAM
out SPH,temp
ldi temp, low(RAMEND)
out SPL,temp
ser temp ; PortB =
out DDRB,temp ; all outputs
out PORTB,temp ; PortB LED's = off
ldi temp,0b11111100 ; PortD0 and PortD1 = inputs, all
out DDRD,temp ; other pins = output
com temp ; temp now becomes 0b00000011
out PORTD,temp ; PortD0 and PortD1 have pullups
; enabled, remaining pins output 0's
ldi temphi,0x00 ; setup BAUD rate for 9600 @ 3.6864MHz
ldi templo,0x17
sts UBRR0H,temphi ; "sts" is used because UBRR0H is not within reach
; of "out"
out UBRR0L,templo ; UBRR0L is within reach of "out"
;***********************************************************************************
; USART
; Frame format = 1 start bit, 8 data bits, no parity bit, 1 stop bit (8,N,1)
;
; Registers UCSR0B and UCSR0C are already setup for this Frame format at RESET.
;
; No programming action is required for the above registers...if any settings need
; to be changed I need to remember that UCSR0C cannot be accessed via the OUT
; instruction, use STS instead.
;
;***********************************************************************************
;
init:
clr bitcntr ; initialize appropriate registers
clr keydata
clr parity
clr flags
main:
call sclhi ; make sure SCL is high to start off
call scllo ; look for SCL going low
inc bitcntr
cpi bitcntr,0x01 ; is this a Start bit?
breq startbit ; yes, go check Start bit
cpi bitcntr,0x0A ; is this a Data bit?
brlt databit ; yes, go get Data bit
breq paritybit ; no, go get and calculate parity
cpi bitcntr,0x0B ; is this a Stop bit
breq stopbit ; yes, go check Stop bit
startbit:
sbic PIND,PD1 ; Start bit = 0?
rjmp starterr ; no...go send error message
rjmp main ; yes..go look for data bits
databit:
bclr 0 ; pre-clear Carry flag
sbic PIND,PD1 ; SDA = 0?
bset 0 ; no, then set Carry flag
brbc 0,rotate ; now check status of Carry flag for parity chk
in flags,SREG ; save
push flags ; status register
inc parity ; for parity check later
pop flags ; restore
out SREG,flags ; status register
rotate:
ror keydata ; rotate contents of Carry flag into keydata
rjmp main ; go get another bit
;
; Parity checking.....
; this routine is based on Odd Parity; meaning if there are an even number of 1's
; in the data byte the Parity bit will be 1 so that an odd number of 1's now exist,
; if there are already an odd number of 1's then the Parity bit will be 0.
;
; I will keep track of Parity by incrementing the parity register once for every 1
; that is received on the data line. If there are an odd number of 1's the lsb of
; parity register will always be 1
;
; Therefore, if the lsb of the parity register = 1 (odd number of 1's) then the
; Parity bit should be 0 (there were an odd number of 1's already. This condition
; can be checked by exclusive ORing those 2 bits. As long as they are opposite
; the parity checks out good.
;
paritybit:
in flags,PIND ; SDA = PD1
ror flags ; rotate SDA into bit 0 position
andi flags,0x01 ; clear all but bit 0 position
andi parity,0x01 ; again, clear all but bit 0
eor flags,parity ; exclusive-or to find out if lsb's of flags
; and parity the same
breq parerr ; report error if they are the same
rjmp main ; return and get stop bit now
stopbit:
sbis PIND,PD1 ; SDA = 1?
rjmp stoperr ; no, go report error
cpi keydata,0xAA ; BAT message from keyboard?
brne chkforbrkcode ; no, check for next code of interest
ldi ZH,high(2*batmsg) ; pre-load BAT message address
ldi ZL,low(2*batmsg)
call sendmsg ; go send message
rjmp init ; go look for another key press
chkforbrkcode:
cpi keydata,0xF0 ; Break code (0xF0):?
brne sendkeychar ; no, go send ASCII key code
ldi ZH,high(2*brkmsg) ; yes, now pre-load Break message address
ldi ZL,low(2*brkmsg)
call sendmsg ; go send message
rjmp init ; look for another keypress
starterr:
ldi ZH,high(2*sterrmsg) ; load high part of message address in ZH
ldi ZL,low(2*sterrmsg) ; load low part of message address in ZL
call senderrmsg
parerr:
ldi ZH,high(2*parerrmsg); load high part of message address in ZH
ldi ZL,low(2*parerrmsg) ; load low part of message address in ZL
call senderrmsg
stoperr:
ldi ZH,high(2*sperrmsg) ; load high part of message address in ZH
ldi ZL,low(2*sperrmsg) ; load low part of message address in ZL
call senderrmsg
;********* Subroutines **************
scllo:
sbic PIND,PD0 ; PD0(SCL) = 0?
rjmp scllo ; no...continue checking SCL
ret ; yes...return main program
sclhi:
sbis PIND,PD0 ; PD0(SCL) = 1?
rjmp sclhi ; no...continue checking SCL
ret ; yes..return to main program
getsda:
bclr 0 ; pre-clear the Carry flag
sbic PIND,PD1 ; check state of SDA
bset 0 ; if SDA = 1, set Carry flag
ret ; if SDA = 0. Carry flag is already cleared
;******************************************************************************
senderrmsg:
; the calling program will have to load ZH and ZL with the appropriate message
; address
sbi UCSR0B,TXEN0 ; enable transmitter, TXEN = 1
getmsg:
lpm R0,Z+ ; retrieve character from program memory
tst R0 ; is it the last character?
breq loop ; yes..then loop forever
out UDR0,R0 ; no...transmit character
call chkxmitend ; wait for transmit to finish
rjmp getmsg ; get next character
loop:
cbi UCSR0B,TXEN0 ; disable transmitter
rjmp loop ; program stops here when error occurs, user
; will have to reset the STK500 and try again
;******************************************************************************
sendmsg:
; the calling program will have to load ZH and ZL with the appropriate message
; address
sbi UCSR0B,TXEN0 ; enable transmitter, TXEN = 1
getmsg1:
lpm R0,Z+ ; retrieve character from program memory
tst R0 ; is it the last character?
breq continue ; yes..then loop forever
out UDR0,R0 ; no...transmit character
call chkxmitend ; wait for transmit to finish
rjmp getmsg1 ; get next character
continue:
cbi UCSR0B,TXEN0 ; disable transmitter
ret
;******************************************************************************
sendkeychar:
sbi UCSR0B,TXEN0 ; enable transmitter, TXEN = 1
ldi ZH,high(2*ascii) ; load high part of key character address in ZH
ldi ZL,low(2*ascii) ; load low part of key character address in ZL
add ZL,keydata ; add keydata offset to ZL register
brcc getchar ; if no carry, load key character in R0
inc ZH ; if there is a carry, increment ZH register
getchar:
lpm ; load appropriate key character into R0
out UDR0,R0 ; no...transmit character
call chkxmitend ; wait for transmit to finish
cbi UCSR0B,TXEN0 ; disable transmitter
ret
;******************************************************************************
chkxmitend:
sbis UCSR0A,UDRE0 ; data register empty flag = 1
rjmp chkxmitend ; no...keep checking
ret ; yes..continue with program
;************* MESSAGES ******************************************************
batmsg:
.db " Basic Assurance Test complete ",0
brkmsg:
.db " Break ",0
; ************* ERROR MESSAGES ************************************************
sterrmsg:
.db "Start Bit Error",0
parerrmsg:
.db "Parity Bit Error"
.db 0,0
sperrmsg:
.db "Stop Bit Error"
.db 0,0
; ************ KEY CHARACTERS ************************************************
ascii:
; 0x00 - 0x0F
.db '?','F','?','F','F','F','F','F','?','F','F','F','F','T','`','?'
; 0x10 - 0x1F
.db '?','L','L','?','L','q','1','?','?','?','z','s','a','w','2','?'
; 0x20 - 0x2F
.db '?','c','x','d','e','4','3','?','?','S','v','f','t','r','5','?'
; 0x30 - 0x3F
.db '?','n','b','h','g','y','6','?','?','?','m','j','u','7','8','?'
; 0x40 - 0x4F
.db '?',',','k','i','o','0','9','?','?','.','/','l',';','p','-','?'
; 0x50 - 0x5F
.db '?','?',''','?','[','=','?','?','C','R','E',']','?','\','?','?'
; 0x60 - 0x6F
.db '?','?','?','?','?','?','B','?','?','1','?','4','7','?','?','?'
; 0x70 - 0x7F
.db '0','.','2','5','6','8','E','N','F','+','3','-','*','9','S','?'
; 0x80 - 0x83
.db '?','?','?','F'
|
|