0% found this document useful (0 votes)
77 views12 pages

Experiment #2: Assembly Programming Using Subroutines & Stacks

This document discusses experiment #2 which focuses on using subroutines and stacks in assembly programming. It provides background on error detection and correction techniques such as parity bits and cyclic redundancy checks (CRCs). It emphasizes planning programs using pseudocode or flowcharts before writing code. It provides an example problem, pseudocode, and flowchart for a program to sort three numbers from largest to smallest. Finally, it outlines expectations for coding assignments, including using registers efficiently, documentation, and simulation/debugging.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
77 views12 pages

Experiment #2: Assembly Programming Using Subroutines & Stacks

This document discusses experiment #2 which focuses on using subroutines and stacks in assembly programming. It provides background on error detection and correction techniques such as parity bits and cyclic redundancy checks (CRCs). It emphasizes planning programs using pseudocode or flowcharts before writing code. It provides an example problem, pseudocode, and flowchart for a program to sort three numbers from largest to smallest. Finally, it outlines expectations for coding assignments, including using registers efficiently, documentation, and simulation/debugging.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

EXPERIMENT #2:

ASSEMBLY PROGRAMMING USING SUBROUTINES & STACKS


2.1 OBJECTIVE

In the first experiment, you got familiar with the AtmelStudio7 programming and Proteus simulation
environment. You you wrote, debugged, and executed a simple assembly code to move data and control
I/O ports. During this expertiment the use of microcontroller arithmetic/logic, jump/branch instructions,
subroutines, and stacks will be mastered in order to develop complex code segments with powerful
functions, for which some time needs to be invested in planning as introduced in Section 2.2.2.

2.2 PRELIMINARY WORK

2.2.1 Background of Error Detection and Correction Techniques

Error detection and correction techniques are commonly used in computer science and
telecommunication applications. These techniques’ main aim is to develop a reliable delivery method of
digital data over unreliable wired or wireless communication channels. A communication is subject to
attenuation and noise, and thus errors may be introducted during the communication between the source
and the receiver.

2.2.1.1 Error Detection

Error detection is the name of the process of detecting of errors caused by noise or other impariments
during transmission from the source to the receiver, or in storing critical information. Most commonly used
error detection schemes are Repetition codes, Partiy bits, Checksums, Cyclic redundancy checks.

2.2.1.1.1 Parity Bits

A parity bit, or check bit is a bit added to the end of a string of binary code, which indicates whether
the number of bits in the string with the value one is even or odd. In other words total number of ones
within the string is even or odd. Parity bits are the simplest form of error detecting code.
Table 1. Parity Bit Scheme
Parity bit scheme Character Bits Parity Bit Data with Parity Bit
Odd Parity Bit 0000000 1 00000001
1101101 0 11011010
1000100 1 10001001
1111111 0 11111110
Even Parity Bit 0000000 0 00000000
1111001 1 11011011
1001000 0 10001000
1111111 1 11111111

2.2.1.1.2 Cyclic Redundancy Check (CRC)

Cyclic Redundancy Check (CRC) is a verification method that is used to ensure that data being sent is
not corrupted during transfer over communication channels. CRC codes are polynomial functions that
can be represented in binary e.g. x5+x3+x2+x0= (101101)2. The CRC algorithm, was first proposed by
W.Wesley Peterson (Brown, 1961), computes a CRC code for each specific data set that is sent through
by utilizing a polynomial key and the transferred data. CRC uses systematic cyclic codes that encode the
message by adding a fixed-length check value, which is calculated with the use of a polynomial key. The
main advangtage of the CRC is the ability of detecting burst errors within the digital data, which is a


 
common error type in communication, magnetic and optical storage devices. Typically an n-bit CRC
applied to a data block of arbitrary length will detect any single error burst not longer than n bits and will
detect a fraction 1 − 2−n of all longer error bursts. CRC can detect more errors than simple parity bit and
checksum.

The basic description of CRC procedure can be summarized as follows:


1. Transmitter and receiver both agree on a generator polynomial (key), G for CRC calculations.
2. Transmitter:
 Performs modulo 2 division of the original message to be transferred by a generator polynomial
G represented as a binary number (modulo 2 division means subtraction in the division is
replaced by XOR operation), and
 attaches the remainder of the division to the original message as the CRC code for error
correction.
3. Receiver:
 Performs the same division on the received message using the same generator polynomial G
as the receiver, and compares the resulting remainder with the received remainder.  

If a transmitter wants to send a message D with d data bits, a CRC code R with r bits of length is
generated through modulo 2 division. Thus, the remainder is calculated through:

. (1)

The remainder bits are then appended to the original data for the receiver to check, as outlined above.
The multiplication in Equation 1 can be achieved by padding r zeros to the message before doing the
modulo 2 division by G. An example for the CRC code calculation is provided below.
Example:
Consider the following 2-byte data string (message) D, and generator polynomial G:
D = 1101001110110000 G = x³ + x + 1 (in binary this corresponds to 1011)
Hence, the maximum number of bits in the remainder after division, r=3, so three 0’s are first appended
to D before the modulo 2 division. Detailed steps of the modulo 2 division are depicted below:
1101001110110000 000 1011
1011 111100011111001
01100
1011
01110
1011
01011
1011
00001101
1011
01101
1011
01100
1011
01110
1011
01010
1011
0001000
1011
00110  3-bit remainder to be appended to 2 bytes of sent data as CRC code


 
2.2.2 Before Writing a Program

It is a good practice to plan the flow of a program before writing it. This allows major issues
associated with the algorithm to be thought out, and resolved before going down to the next level of detail.
Once the algorithm is solid, writing of the code typically takes much less time. There are many ways to
do a program plan. Using either a pseudocode or a flowchart is very common.

2.2.2.1 Pseudocode Generation

A pseudocode uses an expressive, clear, and concise method to describe an algorithm. It may
have short English phrases, arrows () to indicate storing of data into variables, or it may look somewhat
like a high level programming language without the details of the syntax. Since the pseudocode focuses
on the algorithm to solve the problem instead of the syntax, it abstracts out the problem solving stage
from the code writing stage with appropriate instructions and syntax. Below is an example of a problem
statement and the pseudocode developed for it.

Problem: Write an assembly language source code to be executed on AtMega128 as follows: The
program should read three unsigned numbers at memory addresses $0000, $0001, and $0002, sort them
from largest to smallest, and store them to addresses $0003, $0004, and $0005. i.e. at the end of the
execution, memory addresses $0003 and $0005 should contain the largest and smallest numbers
respectively.

Pseudocode: Note this is just one algorithm to solve this problem. A and B stand for ACCA and ACCB
for convenience, but they do not have to be in a pseudocode.

A  1st Number ($0000)


B  2nd Number ($0001)
If A < B
A  Smallest ($0005)
B  Largest ($0003)
Else
B  Smallest
A  Largest
A  Largest
B  3rd Number ($0002)
If A < B
B  Largest
A  Middle ($0004)
Done.
Else
B  Middle
A  Smallest
If A < B
Done.
Else
B  Smallest
A  Middle
Done.

2.2.2.2 Flowchart

A flowchart illustrates the steps in a process. It contains different symbols to indicate various tasks
that the program has to do. There are many dedicated symbols for different types of tasks, but not all
have to be utilized for the algorithm to be clear. In general the tasks in the regular flow are indicated by


 
rectangular boxes, and a decision point to change the program flow (If-Then-Else, Repeat-Until) is
indicated by diamonds. For example the algorithm described in the previous section can be depicted in
a flowchart as below.

Flowchart:

When completing the coding assignments in sections 2.2.3 and 2.2.4:


 Write a short pseudo-code or draw a flowchart before starting to write your program to outline the
planned program flow.
 Unless stated otherwise, minimize the use of main memory in order to optimize performance and
memory allocation. Maximize the use of CPU registers.
 The code should be well documented.
 Think about if you need to store parameters getting passed to and from the subroutine to absolute
memory addresses. If not, storing them to the stack will allow your subroutines to be reentrant.
 Your assembly code should be first simulated and debugged using Atmel Studio. You should then
develop a model using Proteus to simulate the code with the full system interface in order to save
valuable time in the lab spent on debugging and validation. All preliminary work will be submitted at
the beginning of your scheduled laboratory session.

2.2.3 Simple Comparison and Parity Calculation (Input: Switches, Output: LCD)

Write a program that reads two different positive numbers from two DIP Switches (8-bits each) on
UNI-DS6 board. The program should also read another input of two control or mode bits (2 of the switches
on another DIP-switch pack) to control the processing that will be done on the two numbers. The program


 
will first read all control and I/O values and save them to registers, and then display some data processing
results on the UNI-DS6 LCD display:
Mode
Processing Result (on LCD display)
Control
0 First number appended with an odd parity bit – displayed result should be binary
1 First number appended with an even parity bit – displayed result should be binary
The larger of the two input numbers in Hexadecimal on the LCD Screen - if numbers are
2
equal output should display “EQ” instead on the LCD display
The difference of the two input numbers on the LCD screen in hexadecimal and in binary
at the same time. Use positive logic; that means make sure you are subtracting smaller
3 number from the bigger number and providing the magnitude of the difference. Inputs
from DIP switches should be interchangeable i.e. the result should display the same
regardless of which of the DIP switch inputs is subtracted from the other.

Actual processing of each mode should be implemented within a subroutine, i.e. once the
inputs are read from the pins, one of the four subroutines should be called, depending on the control
value. Each subroutine should determine the string(s) that will be printed to the LCD string, and call
another LCD subroutine to output the result on LCD. After the subroutine is done, the code should go
back to the main portion and read the input pins again (in an infinite loop). Remember to initialize the
stack pointer at the beginning of your code for correct operation. The program should work seamlessly
on UNI-DS6 development board i.e. when input data and/or control bits are changed through the switches
on the board, the corresponding processing result should be “immediately” displayed on the LCD. It is
expected that you will read, understand, and use necessary portions of the LCD driver assembly code
provided in the APPENDIX. Provide the following in your prelab report:
i. Flowchart or pseudo-code for the program
ii. Annotated assembly code (also annotate main block and the subroutines),
iii. Atmel Studio and Proteus screen captures to demonstrate your simulation based verification
of correct operation in each of the modes of operation. Each screen capture should be
commented carefully to make sure the reader can follow your results. Some of the Atmel Studio
simulation captures should clearly show the state of the stack and the stack pointer after each
subroutine call. Proteus simulations should clearly show correct LCD outputs, using a
standard 16x2 alphanumeric LCD library component (check the earlier Lab Tutorial to verify
you picked the correct component with the correct interface pins).

2.2.4 4-bit CRC Code Generation (Input: Switches, Output: LCD) 

Write an assembly program that calculates a 4 bit CRC code with a polynomial of x4 + x + 1
(named CRC-4-ITU). The program should call the first subroutine to read the input from a DIP switch
pack (8-bit input) on UNI-DS6; then it should calculate the 4-bit CRC code in the second subroutine. It
should finally display the CRC code on UNI-DS6 LCD display in a third subroutine. The output should
be displayed in two formats: Binary and HEX. It is expected that you will read, understand, and use
necessary portions of the LCD driver assembly code provided in the APPENDIX. The program should
then go back to the beginning and keep running the three routines in an infinite loop. Provide the
following in your prelab report:
iv. Flowchart or pseudo-code for the program,
v. Annotated assembly code,
vi. Atmel Studio and Proteus screen captures to demonstrate your simulation based verification
of correct operation in each of the four modes of operation. Each screen capture should be
commented on carefully to make sure the reader can follow your simulation results. Atmel
Studio simulations should also clearly show the state of the stack and the stack pointer after
each subroutine call. Proteus simulations should clearly show correct LCD outputs.


 
2.3 EXPERIMENTAL WORK

2.3.1 Code 1 Verification:

Demonstrate your code operation from Section 2.2.3 on UNI-DS6 development board to the lab
instructor. Be ready to also demonstrate your Atmel Studio and Proteus simulations if asked by
the instructor.

2.3.2 Code 2 Verification:

Demonstrate your code operation from Section 2.2.4 on UNI-DS6 development board to the lab
instructor. Be ready to also demonstrate your Atmel Studio and Proteus simulations if asked by
the instructor.

Note: At a minimum you will be required to demonstrate that you are able to explain the
subroutine calls in your code segments and stack operations in calling and returning from these
calls.

REFERENCES:

Brown, W. Atmega128, Atmel. "8-bit Microcontroller datasheet. 2003." USA, California: Atmel
Corporation.

W. (1961). Cyclic Codes for Error Detection. Proceedings of the IRE , 49 (1), 228-235.

Barnett, Richard, Sarah Cox, and Larry O’Cull. Embedded C programming and the Atmel AVR.
Cengage Learning, 2006.


 
APPENDIX: LCD Driver Code for Interfacing the Standard 2x16 LCD on UNI-DS6 Board

; *************************************************************************** 
;   LCD Application main.asm ‐‐ LCD with an Atmel ATmega128 processor 

; *************************************************************************** 
;        File:    main.asm 
;        Date:    13/03/2016 
;      Target:    ATmega128 
;    Hardware:    UN‐DS6 Development Board 

;     Summary:    4‐bit LCD interface, PortC D7‐D4 to LCD 


; *************************************************************************** 
 
.INCLUDE "m128def.inc" 
 
 
.equ    fclk                = 8000000      ; system clock frequency (for delays) 
; register usage 
.def    temp                = R16           ; temporary storage 
 
; LCD interface  
.equ    lcd_D7_port         = PORTC         ; lcd D7 connection 
.equ    lcd_D7_bit          = PORTC7 
.equ    lcd_D7_ddr          = DDRC 
 
.equ    lcd_D6_port         = PORTC         ; lcd D6 connection 
.equ    lcd_D6_bit          = PORTC6 
.equ    lcd_D6_ddr          = DDRC 
 
.equ    lcd_D5_port         = PORTC         ; lcd D5 connection 
.equ    lcd_D5_bit          = PORTC5 
.equ    lcd_D5_ddr          = DDRC 
 
.equ    lcd_D4_port         = PORTC         ; lcd D4 connection 
.equ    lcd_D4_bit          = PORTC4 
.equ    lcd_D4_ddr          = DDRC 
 
.equ    lcd_E_port          = PORTB         ; lcd Enable pin 
.equ    lcd_E_bit           = PORTB5 
.equ    lcd_E_ddr           = DDRB 
 
.equ    lcd_RS_port         = PORTB         ; lcd Register Select pin 
.equ    lcd_RS_bit          = PORTB2 
.equ    lcd_RS_ddr          = DDRB 
 
; LCD module Lines 
.equ    lcd_LineOne         = 0x00          ; line 1 
.equ    lcd_LineTwo         = 0x40          ; line 2 
 
; LCD Defined instructions 
.equ    lcd_Clear           = 0b00000001    ; ASCII 'space' for all characters 
.equ    lcd_Home            = 0b00000010    ; first position on first line 
.equ    lcd_EntryMode       = 0b00000110    ; shift cursor from left to right on read/write 
.equ    lcd_DisplayOff      = 0b00001000    ; turn display off 
.equ    lcd_DisplayOn       = 0b00001100    ; display on, cursor off, don't blink character 
.equ    lcd_FunctionReset   = 0b00110000    ; reset the LCD 
.equ    lcd_FunctionSet4bit = 0b00101000    ; 4‐bit data, 2‐line display, 5 x 7 font 
.equ    lcd_SetCursor       = 0b10000000    ; set cursor position 


 
 
; ****************************** Reset Vector ******************************* 
.org    0x0000 
    jmp     start                           ; jump over Interrupt Vectors, Program ID etc. 
 
;******************************* Program ID ********************************* 
.org    INT_VECTORS_SIZE 
 
text: 
.db         "Hello",0 
 
; ****************************** Main Program Code ************************** 
start: 
; initialize the stack pointer to the highest RAM address 
    ldi     temp,low(RAMEND) 
    out     SPL,temp 
    ldi     temp,high(RAMEND) 
    out     SPH,temp 
 
; configure the microprocessor pins for the data lines 
    sbi     lcd_D7_ddr, lcd_D7_bit          ; 4 data lines ‐ output 
    sbi     lcd_D6_ddr, lcd_D6_bit 
    sbi     lcd_D5_ddr, lcd_D5_bit 
    sbi     lcd_D4_ddr, lcd_D4_bit 
 
; configure the microprocessor pins for the control lines 
    sbi     lcd_E_ddr,  lcd_E_bit           ; E line ‐ output 
    sbi     lcd_RS_ddr, lcd_RS_bit          ; RS line ‐ output 
 
; initialize the LCD controller 
    call    lcd_init_4d                     ; initialize the LCD display for a 4‐bit interface 
here: 
  IN    R16, PINB; 
   
; display the first line of information 
    ldi     ZH, high(text)        ; point to the information that is to be displayed 
    ldi     ZL, low(text) 
    ldi     temp, lcd_LineOne               ; point to where the information should be displayed 
    call    lcd_write_string_4d 
 
; display the second line of information 
    ldi     ZH, high(text)       ; point to the information that is to be displayed 
    ldi     ZL, low(text) 
    ldi     temp, lcd_LineTwo               ; point to where the information should be displayed 
    call    lcd_write_string_4d 
 
; endless loop 
    rjmp    here 
 
; ****************************** End of Main Program Code ******************* 
 
; ============================== 4‐bit LCD Function Calls ====================== 
; Name:     lcd_init_4d ‐‐ initialize the LCD module for a 4‐bit data interface 
 
lcd_init_4d: 
; Power‐up delay 
    ldi     temp, 100                       ; initial 40 mSec delay 
    call    delayTx1mS 
 
; IMPORTANT ‐ At this point the LCD module is in the 8‐bit mode and it is expecting to receive   
;    8 bits of data, one bit on each of its 8 data lines, each time the 'E' line is pulsed. 


 
;  Since the LCD module is wired for the 4‐bit mode, only the upper four data lines are 
connected to  
;    the microprocessor and the lower four data lines are typically left open.  Therefore, when  
;    the 'E' line is pulsed, the LCD controller will read whatever data has been set up on the 
upper  
;    four data lines and the lower four data lines will be high (due to internal pull‐up 
circuitry). 

;  Fortunately the 'FunctionReset' instruction does not care about what is on the lower four 
bits so   
;    this instruction can be sent on just the four available data lines and it will be 
interpreted  
;    properly by the LCD controller.  The 'lcd_write_4' subroutine will accomplish this if the  
;    control lines have previously been configured properly. 
 
; Set up the RS and E lines for the 'lcd_write_4' subroutine. 
    cbi     lcd_RS_port, lcd_RS_bit         ; select the Instruction Register (RS low) 
    cbi     lcd_E_port, lcd_E_bit           ; make sure E is initially low 
 
; Reset the LCD controller. 
    ldi     temp, lcd_FunctionReset         ; first part of reset sequence 
    call    lcd_write_4 
    ldi     temp, 10                        ; 4.1 mS delay (min) 
    call    delayTx1mS 
 
    ldi     temp, lcd_FunctionReset         ; second part of reset sequence 
    call    lcd_write_4 
    ldi     temp, 200                       ; 100 uS delay (min) 
    call    delayTx1uS 
 
    ldi     temp, lcd_FunctionReset         ; third part of reset sequence 
    call    lcd_write_4 
    ldi     temp, 200                       ; this delay is omitted in the data sheet 
    call    delayTx1uS 
 
; Preliminary Function Set instruction ‐ used only to set the 4‐bit mode. 
; The number of lines or the font cannot be set at this time since the controller is still in 
the  
;   8‐bit mode, but the data transfer mode can be changed since this parameter is determined by 
one  
;   of the upper four bits of the instruction. 
    ldi     temp, lcd_FunctionSet4bit       ; set 4‐bit mode 
    call    lcd_write_4 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
 
; Function Set instruction 
    ldi     temp, lcd_FunctionSet4bit       ; set mode, lines, and font 
    call    lcd_write_instruction_4d 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
 
; The next three instructions are specified in the data sheet as part of the initialization 
routine, 
;   so it is a good idea (but probably not necessary) to do them just as specified and then redo 
them 
;   later if the application requires a different configuration. 
 
; Display On/Off Control instruction 
    ldi     temp, lcd_DisplayOff            ; turn display OFF 
    call    lcd_write_instruction_4d 
    ldi     temp, 80                        ; 40 uS delay (min) 


 
    call    delayTx1uS 
 
; Clear Display instruction 
    ldi     temp, lcd_Clear                 ; clear display RAM 
    call    lcd_write_instruction_4d 
    ldi     temp, 4                         ; 1.64 mS delay (min) 
    call    delayTx1mS 
 
; Entry Mode Set instruction 
    ldi     temp, lcd_EntryMode             ; set desired shift characteristics 
    call    lcd_write_instruction_4d 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
 
; This is the end of the LCD controller initialization as specified in the data sheet, but the 
display 
;   has been left in the OFF condition.  This is a good time to turn the display back ON. 
 
; Display On/Off Control instruction 
    ldi     temp, lcd_DisplayOn             ; turn the display ON 
    call    lcd_write_instruction_4d 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     lcd_write_string_4d 
; Purpose:  display a string of characters on the LCD 
; Entry:    ZH and ZL pointing to the start of the string 
;           (temp) contains the desired DDRAM address at which to start the display 
; Exit:     no parameters 
; Notes:    the string must end with a null (0) 
;           uses time delays instead of checking the busy flag 
 
lcd_write_string_4d: 
; preserve registers 
    push    ZH                              ; preserve pointer registers 
    push    ZL 
 
 
; set up the initial DDRAM address 
    ori     temp, lcd_SetCursor             ; convert the plain address to a set cursor 
instruction 
    call   lcd_write_instruction_4d         ; set up the first DDRAM address 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
 
; write the string of characters 
lcd_write_string_4d_01: 
    ld     temp, Z                       ; get a character 
    cpi     temp,  0                        ; check for end of string 
    breq    lcd_write_string_4d_02          ; done 
 
; arrive here if this is a valid character 
    call    lcd_write_character_4d          ; display the character 
    ldi     temp, 80                        ; 40 uS delay (min) 
    call    delayTx1uS 
  inc     zl 
    rjmp    lcd_write_string_4d_01          ; not done, send another character 
 
; arrive here when all characters in the message have been sent to the LCD module 
lcd_write_string_4d_02: 

10 
 
    pop     ZL                              ; restore pointer registers 
    pop     ZH 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     lcd_write_character_4d 
; Purpose:  send a byte of information to the LCD data register 
; Entry:    (temp) contains the data byte 
; Exit:     no parameters 
; Notes:    does not deal with RW (busy flag is not implemented) 
 
lcd_write_character_4d: 
    sbi     lcd_RS_port, lcd_RS_bit         ; select the Data Register (RS high) 
    cbi     lcd_E_port, lcd_E_bit           ; make sure E is initially low 
    call    lcd_write_4                     ; write the upper 4‐bits of the data 
    swap    temp                            ; swap high and low nibbles 
    call    lcd_write_4                     ; write the lower 4‐bits of the data 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     lcd_write_instruction_4d ‐‐ Send a byte of information to the LCD instruction 
register 
lcd_write_instruction_4d: 
    cbi     lcd_RS_port, lcd_RS_bit         ; select the Instruction Register (RS low) 
    cbi     lcd_E_port, lcd_E_bit           ; make sure E is initially low 
    call    lcd_write_4                     ; write the upper 4‐bits of the instruction 
    swap    temp                            ; swap high and low nibbles 
    call    lcd_write_4                     ; write the lower 4‐bits of the instruction 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     lcd_write_4 Send 4‐bits of information to the LCD module 
; Entry:    (temp) contains a byte of data with the desired 4‐bits in the upper nibble 
;           (RS) is configured for the desired LCD register 
;           (E) is low 
;           (RW) is low 
lcd_write_4: 
; set up D7 
    sbi     lcd_D7_port, lcd_D7_bit         ; assume that the D7 data is '1' 
    sbrs    temp, 7                         ; check the actual data value 
    cbi     lcd_D7_port, lcd_D7_bit         ; arrive here only if the data was actually '0' 
 
; set up D6 
    sbi     lcd_D6_port, lcd_D6_bit         ; repeat for each data bit 
    sbrs    temp, 6 
    cbi     lcd_D6_port, lcd_D6_bit 
 
; set up D5 
    sbi     lcd_D5_port, lcd_D5_bit 
    sbrs    temp, 5 
    cbi     lcd_D5_port, lcd_D5_bit 
 
; set up D4 
    sbi     lcd_D4_port, lcd_D4_bit 
    sbrs    temp, 4  
    cbi     lcd_D4_port, lcd_D4_bit 
 
; write the data 
                                            ; 'Address set‐up time' (40 nS) 
    sbi     lcd_E_port, lcd_E_bit           ; Enable pin high 
    call    delay1uS                        ; implement 'Data set‐up time' (80 nS) and 'Enable 
pulse width' (230 nS) 

11 
 
    cbi     lcd_E_port, lcd_E_bit           ; Enable pin low 
    call    delay1uS                        ; implement 'Data hold time' (10 nS) and 'Enable 
cycle time' (500 nS) 
    ret 
 
; ============================== End of 4‐bit LCD Subroutines =============== 
 
; ============================== Time Delay Subroutines ===================== 
; Name:     delayYx1mS Delay of (YH:YL) x 1 mS 
delayYx1mS: 
    call    delay1mS                        ; delay for 1 mS 
    sbiw    YH:YL, 1                        ; update the the delay counter 
    brne    delayYx1mS                      ; counter is not zero 
 
; arrive here when delay counter is zero (total delay period is finished) 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     delayTx1mS Provide a delay of (temp) x 1 mS 
delayTx1mS: 
    call    delay1mS                        ; delay for 1 mS 
    dec     temp                            ; update the delay counter 
    brne    delayTx1mS                      ; counter is not zero 
 
; arrive here when delay counter is zero (total delay period is finished) 
    ret 
 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     delay1mS ‐‐ Delay of 1 mS 
delay1mS: 
    push    YL                              ; [2] preserve registers 
    push    YH                              ; [2] 
    ldi     YL, low (((fclk/1000)‐18)/4)    ; [1] delay counter 
    ldi     YH, high(((fclk/1000)‐18)/4)    ; [1] 
 
delay1mS_01: 
    sbiw    YH:YL, 1                        ; [2] update the the delay counter 
    brne    delay1mS_01                     ; [2] delay counter is not zero 
; arrive here when delay counter is zero 
    pop     YH                              ; [2] restore registers 
    pop     YL                              ; [2] 
    ret                                     ; [4] 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     delayTx1uS Delay of (temp) x 1 uS with a 8 MHz clock frequency 
delayTx1uS: 
    call    delay1uS                        ; delay for 1 uS 
    dec     temp                            ; decrement the delay counter 
    brne    delayTx1uS                      ; counter is not zero 
; arrive here when delay counter is zero (total delay period is finished) 
    ret 
; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 
; Name:     delay1uS 
; Purpose:  Delay of 1 uS with a 8 MHz clock frequency 
delay1uS: 
    push    temp                            ; [2] Consume clock cycles 
    pop     temp                            ; [2] 
    ret                                     ; [4] 
; ============================== End of Time Delay Subroutines ==============

12 
 

You might also like