Lab 4 { Data Manipulation & the LCD Solution

$30.00

Description

SECTION OVERVIEW

Complete the following objectives:

  • Understand the basics of data manipulation in AVR assembly.

  • Learn the basic steps of initializing a program, such as setting up the stack pointer, initializing registers, con guring peripheral devices, etc.

  • Use the X, Y, and Z pointers to perform indirect addressing.

  • Declare constant data in the program memory, and iteratively move that data into the data memory.

  • Use pre-written library functions to interact with a peripheral device.

PRELAB

To complete this prelab, you may nd it useful to look at the AVR Starter Guide and the AVR Instruction Set Manual. If you consult any online sources to help answer the prelab questions, you must list them as references in your prelab.

  1. What is the stack pointer? How is the stack pointer used, and how do you initialize it? Provide pseudocode (not actual assembly code) that illustrates how to initialize the stack pointer.

  1. What does the AVR instruction LPM do, and how do you use it? Provide pseudocode (not actual assembly code) that shows how to setup and use the LPM instruction.

  1. Take a look at the de nition le m128def.inc (This le can be found in the Solution Explorer ! Dependencies folder in Atmel Studio, assuming you have an Assembler project open and you have already built an assembly program that includes this de nition le. Two good examples of such a project would be your Lab 1 and Lab 3 projects.) What is contained within this de nition le? What are some of the bene ts of using a de nition le like this? Please be speci c, and give a couple examples if possible.

BACKGROUND

Introduction

For this lab, you will learn to interact with the LCD included on the mega128 microcontroller board. In order to use the LCD, you will rst need to learn how to properly initialize an assembly program. Next, you will learn how to move data from the program memory into the data memory, and then display that data as characters on the LCD.

To help you along the way, a skeleton le (ece375-L4 skeleton.asm) has been provided for this lab. Similar to the Lab 2 skeleton C le, this le contains some code that is already written, and some comments indicating where you need to add some code of your own. This skeleton le also provides a good foundation for writing well-structured and well-commented assembly code, as de ned in the AVR Starter Guide, and as required for full credit on your lab write-up.

Initialization

A program initialization (INIT) consists of any code that is only run once, at the beginning of the program. This code does not belong in the MAIN portion of the program, which is why most assembly programs begin with INIT and not MAIN. INIT is not a function (you wouldn’t ever jump to it from within the MAIN part of your program), but rather a collection of instructions that are executed at power on (and at reset). Traditionally, INIT nishes with a jump to MAIN.

There are several things that should be included in the INIT section of a program. The rst and foremost is the initialization of the stack pointer. The stack in AVR operates in a higher-to-lower address fashion, meaning that the most recent element placed on the stack is at a lower address than the previous element placed on the stack. Therefore, the stack pointer should initially point to the upper end of the data memory; in other words, to the location with the highest address in the data memory space. See the AVR Starter Guide for more information about the stack.

In addition to initializing the stack pointer, which must be done for your program to perform function calls correctly, there are several other things that are typically initialized within the INIT section of a program, such as:

  • I/O ports

  • Timer/counters

  • Interrupts

  • Peripheral devices

©2020 Oregon State University Winter 2020 Page 1

Lab 4 { Data Manipulation & the LCD

ECE 375

The exact contents of INIT will depend on which features of the mega128 board you need to use in your program. For this lab, you will need to initialize the LCD. To do so, you will perform a function call to the LCD initialization subroutine, LCDInit, which is described later in this document. You will also need to move data from the program memory to the data memory in this lab, so you may want to plan on doing that in your INIT section as well.

Using the LCD Driver

To successfully interact with the LCD, you will need to use some pre-written functions that are part of an LCD driver le provided on the lab webpage (LCDDriver.asm). Make sure to download this le and include it into your Atmel Studio project. Unlike the m128def.inc de nition le, which is purely pre-compiler directives, the LCD driver contains actual assembly instruc-tions and thus cannot be included at the beginning of your main program le. As indicated in the AVR Starter Guide, any included code les are included at the end of the main program, i.e. in the last line(s).

After including the LCD driver into your program, you will still need to prop-erly setup and call the functions de ned in the LCD driver to interact with the LCD. (Remember, for any function calls to work correctly, the stack pointer must have already been initialized.) Detailed descriptions of the LCD driver functions are provided in Appendix A at the end of this handout, but here is an overview:

  • LCDInit { Initialize the LCD (call once from within INIT)

  • LCDWrite { Update all characters of both lines

  • LCDWrLn1 { Update all characters of line 1 only

  • LCDWrLn2 { Update all characters of line 2 only

  • LCDClr { Clear (write \space” to) all characters of both lines

  • LCDClrLn1 { Clear all characters of line 1 only

  • LCDClrLn2 { Clear all characters of line 2 only

  • LCDWriteByte { Write directly to a single character of the LCD

  • Bin2ASCII { Convert an 8-bit unsigned value into an ASCII string

There are other functions within the LCD driver, but they simply support the functions listed above, so they should not be called directly from your program.

Data Manipulation

To move data from one memory type to another, you rst must understand how the available memory is organized. The ATmega128 microcontroller has an 8-bit AVR architecture. This means that all registers and data memory locations are 8 bits wide, and all data is handled 1 byte at a time (disregarding certain instructions that can treat certain pairs of registers as one 16-bit \word”).

However, the AVR instructions supported by the ATmega128 are 16 bits wide (or 32 bits wide, for a small number of instructions), and so for e ciency rea-sons the ATmega128’s program memory is 16 bits (2 bytes) wide. This has an important consequence: Unlike pointers to data memory, pointers to program memory initially point to a 16-bit memory location, and you will have to take certain steps to read just the low byte or just the high byte of that location.

When writing (and especially when testing) your program, it is often useful to include some data directly into program memory. For example, imagine you are simulating your program, and you are testing a function which uses several data memory locations as input. Instead of manually entering test input values into the data memory via the Memory window in Atmel Studio, you can use the

.DB (Data Byte) directive to have the compiler place your test values into the program memory during compilation, and then write a simple loop or function that copies your test values from program memory into data memory at runtime.

The following example shows how to place data into the program memory:

DATA:

.DB $05, $F4

The hexadecimal values $05 and $F4 can then be accessed using the label DATA, which the compiler converts to a program memory address. To read this data, use the LPM (Load Program Memory) instruction. You can also enter strings (i.e., a sequence of ASCII characters) into program memory using the .DB directive; just take a look at the Lab 4 skeleton le to see how.

Note: Since the program memory is 16 bits wide, the total amount of data you specify using a single instance of the .DB directive should be an integer multiple of 16 bits. Otherwise, the compiler will pad the program memory with an extra byte of data, usually $FF.

Movement within data memory is accomplished using variations of load and store instructions, and with two main addressing modes: direct addressing and indirect addressing. Direct addressing, where a memory address is provided di-rectly as an operand, is useful when you want to move a single byte to/from a single memory location (such as an extended I/O register). Indirect addressing,

©2020 Oregon State University Winter 2020 Page 2

Lab 4 { Data Manipulation & the LCD

ECE 375

where the X, Y, and Z-pointers are an operand and they contain an e ective address, is useful for moving blocks of data into contiguous locations in the data memory. By using indirect addressing in conjunction with a loop, you can e – ciently move lots of data around in memory. The AVR Starter Guide has more information on pointers and indirect addressing. Also, Figure 1 shows some pseudocode you can use to properly read a bunch of data from program memory.

PROCEDURE

You will use three buttons; PD0, PD1, and PD7 to write and clear text on the

LCD display. Write a program that does the following:

  1. When you press PD0: Displays your name on the rst line of the LCD, and a phrase like “Hello World!” (or your partners name) on the second line of the screen.

  1. When you press PD1: The contents should swap so that a phrase like “Hello World” is shown on line 1 and your name is shown on line 2.

  1. When you press PD7: The content should be cleared.

The display should be initially blank. To receive full credit, you must:

  1. properly initialize your program, (2) place your two strings into program memory using the .DB directive. (3) Use two unique strings, for example one string could contain your name, and another your partners. (4) copy the strings from program memory to the appropriate data memory locations using a loop, and (5) there must not be any unintended trailing characters on the LCD.

When you are nished, demonstrate to the TA that you have displayed both strings on the LCD and met these requirements.

Button PD0: Line 1: Your name

Line 2: Hello, World (or partner’s name)

Button PD1: Line 1: Hello, World (or partner’s name)

Line 2: Your name

Button PD7: Line 1:

Line 2:

STUDY QUESTIONS / REPORT

A full lab write-up is required for this lab. When writing your report, be sure to explain in detail what you did and why, indicate any problems you may

  • Z <- program memory address of first character

  • Y <- data memory address of character destination

  • do { mpr <- ProgramMemory[Z], Z++,

DataMemory[Y] <- mpr, Y++ }

while (Z != program memory address after last character)

Figure 1: Pseudocode for String Copy from Program Memory to Data Memory

have encountered, and answer the study questions given below. Your write-up and code must be submitted online by the beginning of next week’s lab. Remember, NO LATE WORK IS ACCEPTED.

Study Questions

  1. In this lab, you were required to move data between two memory types: program memory and data memory. Explain the intended uses and key di erences of these two memory types.

  1. You also learned how to make function calls. Explain how making a function call works (including its connection to the stack), and explain why a RET instruction must be used to return from a function.

  1. To help you understand why the stack pointer is important, comment out the stack pointer initialization at the beginning of your program, and then try running the program on your mega128 board and also in the simulator. What behavior do you observe when the stack pointer is never initialized? In detail, explain what happens (or no longer happens) and why it happens.

CHALLENGE

Not being content with displaying a static message, you would like to add some air by displaying a scrolling, marquee-style message. Modify your program so that the text scrolls across the two lines of the LCD, from left to right and right to left for PD5 and PD6, respectively. Include some wait time (delay) between each \scroll” event, so that there is enough time to read the characters in their current position before they move again.

©2020 Oregon State University Winter 2020 Page 3

Lab 4 { Data Manipulation & the LCD

ECE 375

When a character reaches the end of a line, display it next at the beginning of the opposite line. In other words, a character at index 15 of line 1 will move to index 0 of line 2, and a character at index 15 of line 2 will move to slot 0 of line 1. Here is an example of the required display and motion, where the ‘ ’ character signi es a space:

  1. Line 1: _____My_Name_is_

Line 2: _______Jane_Doe_

(wait for .25 seconds)

  1. Line 1: ______My_Name_is

Line 2: ________Jane_Doe

(wait for .25 seconds)

  1. Line 1: e______My_Name_i

Line 2: s________Jane_Do

(wait for .25 seconds)

  1. Line 1: oe______My_Name_

Line 2: is________Jane_D

etc.

Next, that data is actually written out to the LCD. So, for this function to work properly, you must rst move the data (e.g., an ASCII string) that you want displayed on the LCD into the appropriate locations in data memory. Then, call the function as follows:

rcall LCDWrite ; Write to both lines of the LCD

LCDWrLn1

This function writes data to the rst/top line of the LCD. First, the data is retrieved from data memory addresses $0100 { $010F, and then the data is written out to the rst line of the LCD.

To use this function, make sure the data you want to display is in the appro-priate locations in data memory, and then call the function as follows:

rcall LCDWrLn1 ; Write to first line (Line 1) of the LCD

LCDWrLn2

Use the same two strings from the required part of the lab as the strings that you make scroll. If you complete this challenge, demonstrate it to the TA, and submit your challenge code online with your regular code and your lab write-up.

APPENDIX A { LCD FUNCTIONS

LCDInit

This subroutine initializes the serial interface that is used to communicate with the LCD, and also initializes the display itself, con guring it to display 2 lines/rows of 16 characters (2×16). This function can be called directly, e.g.:

rcall LCDInit ; Initialize LCD peripheral interface

LCDWrite

This function writes data to both lines of the LCD. First, line data is retrieved from the following data memory addresses:

Line 1: $0100 { $010F

Line 2: $0110 { $011F

This function writes data to the second/bottom line of the LCD. First, the data is retrieved from data memory addresses $0110 { $011F, and then the data is written out to the second line of the LCD.

To use this function, make sure the data you want to display is in the appro-priate locations in data memory, and then call the function as follows:

rcall LCDWrLn2 ; Write to second line (Line 2) of the LCD

LCDClr

This subroutine clears both lines of the LCD. Speci cally, it writes the \space” character (ASCII value $20) to data memory locations $0100 { $010F and $0110 { $011F, and then writes to both lines of the LCD. To call it, use:

rcall LCDClr ; Clear both lines of the LCD

LCDClrLn1

This subroutine works similarly to LCDClr, except it only clears the rst/top line of the LCD.

rcall LCDClrLn1 ; Clear first line (Line 1) of the LCD

©2020 Oregon State University Winter 2020 Page 4

Lab 4 { Data Manipulation & the LCD

ECE 375

LCDClrLn2

This subroutine works similarly to LCDClr, except it only clears the sec-ond/bottom line of the LCD.

rcall LCDClrLn2 ; Clear second line (Line 2) of the LCD

LCDWriteByte

This function allows you to write a single ASCII character (or byte) to anywhere on the LCD. It allows direct control over where data is displayed on the LCD, and does not require anything to be stored in data memory rst (unlike many of the previous functions). There are three registers that must be loaded with input parameters before this function is called:

  • mpr { Contains the byte/character that you want to display on the LCD (must be a value between 0 and 255)

  • line { Contains the line number where you want to display the byte (must be either 1 for rst line, or 2 for second line))

  • count { Contains the index number where you want to display the byte (must be a number between 0 and 15, with 0 specifying the leftmost po-sition). Note: The LCD actually accepts index values up to 39, but only indexes 0 through 15 are actually visible on the display. Any index value between 16 and 39 will result in the character being display \o screen”

The following example uses the LCDWriteByte function to write the character ‘D’ to Line 2, index 7 of the LCD:

; load parameters for LCDWriteByte

ldi

mpr, ‘D’

;

load

character ‘D’

into

mpr

ldi

line, 2

;

load

line number 2

into

line register

ldi

count, 7

; load index 7 into count register

; call LCDWriteByte function

rcall

LCDWriteByte ; write character to LCD

Bin2ASCII

This function converts an unsigned 8-bit value into its numerically equivalent ASCII string, i.e. from 13810 ! \138″. The resulting string (with length between

1 and 3 bytes) is stored in the data memory. This function is useful for displaying a value that changes throughout the course of a program (such as a counter).

Two registers must be loaded with input parameters before this function is called, and a third register must be available to store a returned value:

  • mpr { Contains the 8-bit value that will be converted

  • XH:XL { Contains the beginning 16-bit data memory address where the ASCII string will be stored

  • count { Will contain the return value (the length of the created string)

The following example uses the Bin2ASCII function to convert the value 13810 into \138″, and then store it in data memory beginning at location $0112:

; load parameters for Bin2ASCII

ldi

mpr, 138

;

load to-be-converted value into mpr

ldi

XL, low($0112)

;

load X with beginning address

ldi

XH, high($0112)

; of where result will be stored

; call Bin2ASCII function

rcall

Bin2ASCII

; convert value in ASCII

After the above example is executed, the data memory locations $0112, $0113, and $0114 will contain the characters ‘1’, ‘3’, and ‘8’, respectively, and count will contain the value 3.

If a smaller value like 7510 had instead been converted, only locations $0112 and $0113 would contain characters, and count would have a value of 2.

©2020 Oregon State University Winter 2020 Page 5


error: Content is protected !!