[go: up one dir, main page]

0% found this document useful (0 votes)
37 views68 pages

Embedded Systems Lab 06

This document provides instructions for setting up the Code::Blocks IDE with an AVR toolchain to program the ATmega328P microcontroller. The tasks include installing Code::Blocks, downloading and installing the WinAVR compiler, and verifying that avrdude and the compiler directories are correctly added to the system path. Creating and testing a simple "Hello World" project confirms that the setup is complete and ready for embedded software development.

Uploaded by

Aiman
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)
37 views68 pages

Embedded Systems Lab 06

This document provides instructions for setting up the Code::Blocks IDE with an AVR toolchain to program the ATmega328P microcontroller. The tasks include installing Code::Blocks, downloading and installing the WinAVR compiler, and verifying that avrdude and the compiler directories are correctly added to the system path. Creating and testing a simple "Hello World" project confirms that the setup is complete and ready for embedded software development.

Uploaded by

Aiman
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/ 68

NED University of Engineering & Technology

Department of Electrical Engineering

LAB MANUAL

EMBEDDED SYSTEMS
(EE-354) For T.E.(EE)

Instructor name:
Student name:
Roll # Batch:
Semester: Year:
LAB MANUAL
For the course
EMBEDDED SYSTEMS
(EE-354) For T.E.(EE)

Developed by:
Mr. Hassan-ul-Haq, Mr. Hafiz Muhammad Furqan & Ms. Aiman
Spring, 2023

Approved By

The Board of Studies of Department of Electrical Engineering

____________________ ____________________

____________________ ___________________

____________________ ____________________
CONTENTS
Psychomotor: P3
CLO: Duplicate wiring connections for given circuit design while manipulating the embedded software with
C/Assembly IDE in order to change system behavior.
PLO: Lifelong Learning- PLO 12

S. No. Date Title of Experiment Total Marks Signature

To set-up the Code::Blocks IDE with AVR toolchain


1 and test an AVR project on the ATmega328P
microcontroller
To program the AVR ATmega328P I/O (Input/Output)
2* ports for digital input and output

To program the ATmega328P for reading analog input


3* through its Analog-to-Digital Converter ADC module
To utilize the USART (Universal Synchronous /
Asynchronous Receiver /Transmitter) of ATmega328P
4* for transmitting and receiving data though
asynchronous serial communication with PC
To interface an LCD (Liquid Crystal Display) screen
5* with ATmega328P by sending required commands and
data

10

11

12

* RUBRIC based assessment


Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 01

OBJECTIVE:
To set-up the Code::Blocks IDE with AVR toolchain and test an AVR project on the ATmega328P
microcontroller

LAB OUTCOMES:
By the end of the lab, you would be able to:
1) Install the Code::Blocks IDE with an AVR toolchain using (WinAVR)
2) Create an AVR project with the required compiler settings
3) Test the created project on ATmega328P microcontroller using AVRDUDE

BACKGROUND:
The ATmega328P is an 8-bit microcontroller based on the AVR RISC architecture. It is produced by
Microchip Technology. The ATmega328P has 32 KB of flash memory, 2 KB of RAM, and 1 KB of
EEPROM. It has 23 general-purpose I/O pins, a 16-bit timer/counter, a 8-bit timer/counter, a real-time
clock, a pulse-width modulation (PWM) unit, a serial communication interface (USART), a two-wire
interface (TWI), and an analog-to-digital converter (ADC).
The ATmega328P is a popular microcontroller for a variety of projects, including robotics, home
automation, and wearables. It is also used in the Arduino Uno, Arduino Pro Mini, and Arduino Nano
microcontroller development boards. The ATmega328P is a powerful and versatile microcontroller that can
be used in a wide variety of applications. It is a popular choice for hobbyists and professionals alike.
We will be utilizing ATmega328P (Arduino UNO DIP R3) throughout Embedded Systems Lab work and
will program it in C-language. To program the microcontroller, we will use the following:
 Avrdude is a command-line utility for programming AVR microcontrollers. It can be used to write
firmware to the microcontroller's flash memory, erase the flash memory, and read the contents of the
flash memory.
 WinAVR is a software package that includes avrdude, as well as a compiler, assembler, and a number
of other tools for developing applications for AVR microcontrollers.
 Code::Blocks is an integrated development environment (IDE) that can be used to develop applications
for AVR microcontrollers. It includes a graphical user interface for editing and compiling code, as well
as a debugger for stepping through and debugging code.
Avrdude and Winavr are essential tools for developing applications for AVR microcontrollers.
Code::Blocks is a powerful IDE that can make the development process easier.

LAB TASKS
The first two lab tasks guide you to installation of the required IDE and toolchain. The setup files can be
easily found and downloaded from the internet. However, these setup files are available on this shared
folder too.

1
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

TASK 1: To install Code::Blocks IDE with AVR Toolchain


Follow the given step-by-step procedure to first install the Code::Blocks IDE on your system.
1) To download the required setup files from the internet, search CodeBlocks and go to
https://www.codeblocks.org/downloads/. Select Download the binary releases. Select the setup
package depending upon your platform like Microsoft Windows in our case. From the given setup files,
select codeblocks-20.03-setup.exe as shown in Figure 1. Click on one of the Download From options
for example Sourceforge.net. For a 32-bit operating system type, you can select codeblocks-20.03-
32bit-setup.exe.
2) Once the setup file is downloaded, click on it to begin the installation process. Select the default option
and follow the installation steps as suggested by the wizard. After a few minutes of decompressing and
install files, click YES when prompted to start Code::Blocks. This should yield the IDE shown in
Figure. For now, select OK if it fails to auto-detect the compiler.

Figure 1: Download Code::Blocks


Figure 2: Select Source and File Type

Figure 3: Downlod Source


Figure 4: Starting Installation Wizard

2
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 5: License Agreement Figure 6: Plug-in Selection

Figure 7: Destination Folder for Installation Figure 8: Installation Completion

Figure 9: Code::Blocks IDE Figure 10: Ignore Compiler Status

3
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

3) The Code::Blocks IDE is now ready and would allow you to create projects. Since our aim is to develop
AVR Projects, therefore, we first need to install AVR Toolchain. For this we will make use of WinAVR.
4) Search for WinAVR and go to https://sourceforge.net/projects/winavr/files/latest/download. Click on
Download. It will start downloading the latest WinAVR package.
5) Click on the downloaded file to start the setup wizard. Follow the steps shown in figures and select the
correct features to be installed. The WinAVR package has the required avrdude as well as GNU GCC
Compilers.

Figure 11: Source to Download WinAVR Figure 12: Run Dwonloaded Application File

Figure 13: Setup Wizard Figure 14: License Argeement

Figure 15: Destination Folder Figure 16: Choose Components for Installation

4
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 17: Finish Installation

6) Before you create and start working on a project, verify that directories are correctly added to the system
path. To do this, open the command prompt by searching cmd in the Windows search option. In the
command prompt, simply write avrdude. You will see the message shown in Figure if it is correctly
added else you will see the error message shown in Figure.

Figure 18: Testing avrdude through Command Prompt

5
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 19: Error-avrdude is not recognized as internal command

7) After this write path to verify that the required directories shown in the figure are added to the system
path.

Figure 20: Verifying Directories Added in System Path


TASK 2: To create an AVR Project with the required compiler settings
Now you are ready to create and build an AVR project through the Code::Blocks IDE. The following steps
guide you to create the project and set the compiler settings required to build the project. Follow the steps
given below.
a) Creating AVR Project:

1) Open the Code::Blocks. Go to File> Create >Project…. Select AVR Project and click Go. Select
Next.
2) At this step, you are asked to give project title and select folder to create project files. Give any
suitable name to your project for example here, we have called it Project1. A new folder ES Labs is
created on the desktop and is chosen as project folder. After this, click Next.
3) Now, make sure the selected compiler is GNU GCC for AVR and keep the remaining settings as
shown in the figure. Then click Next.
4) The processor we have selected for ES labs is ATmega328P. Select it from the dropdown menu
carefully and keep the rest of the settings as shown in the Figure. Then click Finish
5) You will see your created project Project1 folder created in the Projects tab workspace. Click on the
Project Name > Sources. This folder will show 2 files; main.c and fuse.c, double click on the main.c
file to open it. It is a blank file template created for the AVR project. This is where you will write a
code.

6
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 21: Creating AVR Project

Figure 22: AVR Project Wizard

Figure 24: Compiler Selection


Figure 23: Project Details

7
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 26: Created Empty Project with main.c


Figure 25: Choosing Processor for AVR Project and fuse.c files

b) Compiler Settings:
Before we write instructions for our first test project, let’s first complete the required compiler settings
that will be needed for all AVR projects.
6) Now, go to Settings > Compiler.

Figure 27: Accessing Compiler Settings Option


7) Under the Selected Compiler dropdown menu, select GNU GCC Compiler for AVR and click Set
as default. From the different tabs right below this, select Toolchain executables. For Compiler’s
installation directory, click Auto detect. It should show the auto detected installation path as the one
where WinAVR destination folder was selected earlier. If it fails to do so, you can manually select the
folder by browsing through … option beside Auto-detect. Click OK.

8
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 28: Specifying the Compiler and Installation Directory in Toolchain Executables
This will update a number of things for these settings. Verify each as shown in the following figures.

Figure 29: Specifying the Program Files in the Compiler’s Installation Directory
The Program Files and Additional Paths tabs under the Toolchain executables will be updated as
shown.

Figure 30: Additional Paths in Toolchain Executables

9
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Go to Search directories tab and check that C:WinAVR\avr\include is added in the Compiler tab.

Figure 31: Verifying the Search Directories


Select Compiler settings tab at the left-most. Check the Optimize generated code (for size) [-Os]
option.

Figure 32: Optimization Settings for Compiler


8) Click OK to close the window. The GNU GCC Compiler is set as default with the required compiler
settings needed for now.

c) Add Code and Build Project:


1) Update the main.c file opened in the Editor window with the code given in Figure and save the file
(Ctrl+S). This is a test code that causes the LED on board blink with a delay of 1000msec (1sec).

10
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

#include <avr/io.h>
#define BLINK_DELAY_MS 1000
#include <util/delay.h>
int main (void)
{
// Arduino digital pin 13 (pin 5 of PORTB) for output
DDRB |= 0B100000; // PORTB5
while(1) {
// turn LED on
PORTB |= 0B100000; // PORTB5
_delay_ms(BLINK_DELAY_MS);
// turn LED off
PORTB &= ~ 0B100000; // PORTB5
_delay_ms(BLINK_DELAY_MS);
}
}

Figure 33: Test Code - LED Blink

Figure 34: Updating main.c File with Test Code


2) Now select Build > Build. For a successful build, the Build Log below will show the following
message with 0 errors and 0 warnings.

11
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

Figure 35: Build Log (Successful Build)


3) Upon successful build, you will see some new folders and files added to the project folder. Go to bin
and verify the addition of .hex file as shown in the figure.

Figure 36: Project Folder Updated after Building the Project

Figure 37: Generated Files


Congratulations! You have generated the output file for our code written in the C language. This .hex
file is the one that will be uploaded to the flash memory of microcontroller ATmega328P for
execution of the code.

12
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

TASK 3: To test the project on ATmega328P microcontroller


Arduino UNO R3 DIP uses ATmega328P microcontroller IC. Therefore, we will be using the Arduino
UNO board to program the ATmega328P.
1) Connect Arduino UNO USB cable with your system port. The LEDs on your board should light-up to
verify that connection is made. The system might start installing the required drivers. To verify the
correct connection, go to Device Manager of your system, and check Ports (COM & LPT). If the
device is detected as Arduino UNO or USB Serial Device, note the port it is connected to. Here, we
can see Arduino UNO (COM3) so COM3 is the port. In case the system fails to identify the device,
you need to install Arduino Drivers. For this download the drivers from the internet or access through
the shared drive. Extract the folder contents. Then, right click on the device under Ports in the Device
Manager, and select Update Driver Software… and follow the Wizard. You will have to specify the
path of downloaded driver files.

Figure 38: Arduino UNO R3 DIP


(ATmega328P) Figure 39: Arduino UNO Connection Port

There are 2 approaches to upload the generated code on ATmega328P flash memory. First we will
use AVRDUDE through Command Prompt instructions. Through this, you will be able to understand
the working of AVRDUDE for uploading the file to our microcontroller. Later, we will integrate this tool
to our Code::Blocks to eliminate the manual Command Prompt steps for our ease. Let’s begin the
interesting part of this lab.
a) Uploading code to ATmega328P microcontroller using AVRDUDE through Command
Prompt

1) Open the Command Prompt by typing cmd in the Windows search.


2) First change the current directory to the folder path where Project1.hex file is saved.
For example: C:\Users\Aiman\Desktop\ES Labs\Project1\bin\Debug
To do this, use cd command
cd C:\Users\Aiman\Desktop\ES Labs\Project1\bin\Debug

13
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

3) Now, type avrdude to verify that it is recognized (as has already been done). It display a list
of options available for usage with avrdude command. Read the description written with
each.
4) For now, we will use only the required ones and the details of which are given below. Note
that these are case sensitive.
Table 1: avrdude Usage Options and Description
Options Description Example as applicable to the test case
-p Part No. (To specify the AVR device) In our case, it is m328p (Atmega 328p)

-P Port (To specify the Connection port) In our case, it is COM3


-c Programmer (To specify Programmer In our case, it is Arduino
Type)

-U Memory operation specification. In our case, flash is the memory type where
Required format is: we want to write the code saved in the
<memory type>:w:<file name> generated Project1.hex file
Where, w shows
read the specified file and write it to the
specified device memory
5) Based on the above, write (type, don’t copy-paste) the following command in the Command
Prompt.
avrdude –p m328p –P com3 –c arduino –U flash:w:Project1.hex
It will display some messages shown in Figure below. For successful upload of the hex file,
you will see the LED blinking at the specified rate.

Figure 40: Uploading the .hex File to ATmega328P through Command Prompt avrdude
b) Uploading code to ATmega328P microcontroller using Code::Blocks tool
14
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

To avoid the time-consuming and intimidating Command Prompt interface, we will now add the
AVRDUDE tool to Code::Blocks. Follow the following steps:
1) In your Code:Blocks IDE, click on Tools > Configure Tools > Add. In the Edit Tool
window, set the Name, Executable, Parameters and Working Directory as shown in Figure.
To set the executable browse the specified path of WinAVR folder, and select avrdude.exe.
Note, that in the parameters option, we have written the same avrdude command used earlier.
The working directory is set to be the one containing the .hex file. Click OK and close the
Tools Window.

Figure 41: Required Tool Settings

Figure 42: Added Tool avrdude


2) Now, select Tools from the top menu bar once again. The name of tool just added for
example: avrdude will be available now above the Configure Tools option. Click the
avrdude and that’s it. The Log Window below will show the execution. Once the code is
uploaded to ATmega328P, you will observe the blinking LED.
3) To make it more generalized, we will now add a tool using macros. Here, instead of
specifying the exact file and project names and path, we will use macros that will do the
job and you won’t have to type it again and again for each of your projects.

15
Embedded Systems Lab Lab 01 Setting-up the Code::Blocks IDE with AVR Toolchain
NED University of Engineering & Technology Electrical Engineering Department

4) Go to Tools> Configure Tools. Either Edit the previous tool or use Add to add another
one. This time set the Parameters and Working Directory in terms of macros replacing
project name and path. Click OK.

Figure 43:Adding avrdude with Generic Parameters and Working Directory


5) Now, modify the delay in your main.c file by replacing 1000 to 5000 for 5sec delay. Save it and
Rebuild the file using the Build> Rebuild option. The new file will replace the previously generated
.hex file.
6) Now, upload this one by selecting the newly added Tool through Tools>avrdudedirect. Verify the
successful upload by blinking of LED at 5msec delays.

Congratulations! You have successfully created, built and tested your first AVR project on
ATmega328P using Code::Blocks with AVR Toolchain.

16
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 02
OBJECTIVE:
To program the AVR ATmega328P I/O (Input/Output) ports for digital input and output

LAB OUTCOMES:
By the end of the lab, you would be able to:
1) Identify the AVR ATmega328P I/O ports
2) Utilize DDR (Direct Data Registers) for setting direction of ports
3) Take digital input and set digital output through I/O ports
4) Build required logics for digital inputs and outputs in pure C-language codes and test them on
ATmega328P microcontroller
5) Utilize logical operations for bit-wise manipulation of ports

“There are exactly 10 types of people in the world.


Those who understand binary numbers and those who don’t.”
If this doesn’t make sense to you, you need to brush up your number system’s knowledge which
is a pre-requisite to the course.

BACKGROUND:
ATmega328P Pin Diagram and I/O Ports
Figure 1 shows ATmega328P pin diagram. Here, we can see total 28 pins (14 at each side). Throughout the
labs, we will be exploring functions of these pins and their utilization. Most port pins are multiplexed with
alternate functions for the peripheral features on the device i.e. they have dual roles. Note that enabling the
alternate function of some of the port pins does not affect the use of the other pins in the port as general
digital I/O. However, in this lab, we will focus on the pins that can be utilized for digital input and
output i.e. simple I/O function, so don’t get intimidated by the pin diagram shown in Figure 1.

Figure 1: Pin Diagram of ATmega328P

17
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Ports as General Digital I/O Pins


Digital I/O pins on the AVR microcontroller are grouped into ports. Each port has up to eight pins assigned
to it. However, each pin can be individually configured. So, you can have a mix of input and output pins
on the same port.
The ATmega328P has 23 General Purpose Digital I/O Pins assigned to 3 GPIO Ports (8-bit Ports B, D and
7-bit Port C).
Ports are designated by a letter and pins are numbered starting at 0. For example, the first pin on port B is
named PB0 and the third pin on port D is named PD2. The ports are bi-directional I/O ports. The pin driver
is strong enough (20mA) to drive LED displays directly.
Let’s consider Figure 2 which shows the names and locations of the pins on the ATmega328P for the DIP
package, as well as the corresponding locations on the Arduino Uno board. For those coming from an
Arduino background, there are a couple things to take note. Firstly, you may notice that not all the digital
I/O pins are available for use on the Arduino Uno board. This is because these pins are being used for
alternate functions. PC6 is being used for device reset and PB6 and PB7 are connected to the crystal on the
board. Secondly, even though the analog pins A0 - A5 can be used with the Analog-to-Digital converter,
they may also be used for digital I/O.
Now that we know the names of the pins and where they are located, we will learn how to configure them
using pure C-programming.

Figure 2: Pins on the ATmega328P with the corresponding locations on the Arduino Uno board.

18
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Configuring the Pin


Each port has three I/O registers associated with it, one each for the Data Register – PORTx, Data Direction
Register – DDRx, and the Port Input Pins – PINx. Also notice that each of the I/O registers is 8 bits wide,
and each port has a maximum of 8 pins; therefore each bit of the I/O registers affects one of the pins.
Consequently, each port pin consists of three register bits: DDxn, PORTxn, and PINxn.
The Port Input Pins I/O location is read only, while the Data Register and the Data Direction Register are
read/write.
All registers and bit references in this section are written in general form. A lower case “x” represents the
numbering letter for the port, and a lower case “n” represents the bit number. However, when using the
register or bit defines in a program, the precise form must be used. For example, PORTB3 for bit no. 3 in
Port B, here documented generally as PORTxn.

Figure 3: Relations between the Registers and the Pins of AVR


1) Role of DDRx (Data-Direct Register)
The DDRx I/O register is used solely for the purpose of making a given port an input or output port. To
make any pin of the port an output pin, we write 1 to the corresponding bit of DDRx register. It must be
noted that unless we set the DDRx bits to one, the data will not go from the port register to the pins of the
AVR.
To output data to all of the pins of the Port B, we first put 0b11111111 (0xFF) into the DDRB register to
make all of the pins output. To make a port an input port, we must first put 0s into the DDRx register for
that port, and then bring in (read) the data present at the pins.
Notice that upon reset, all ports have the value 0x00 in their DDR registers. This means that all ports are
configured as input.

Figure 5: Buffer (DDRx.n is used an Enable pin in


Figure 4: The I/O Port in AVR
Figure 4)

19
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

2) Role of PINx (Pin-Register)


To read the data present at the pins, we use PIN register. It must be noted that to bring data into CPU from
pins we read the contents of the PINx register.
3) Role of PORTx (Data-Register)
The PORTx register is used to send data out to pins.

Why program the AVR in C?


Compilers produce hex files that we download into the Flash of the micro-controller. The size of the hex
file produced by the compiler is one of the main concerns because microcontrollers have limited on-chip
Flash.
While Assembly language produces a hex file that is much smaller than C, programming in Assembly
language is often tedious and time consuming. On the other hand, C programming is less time consuming
and much easier to write, but the hex file size produced is much larger than if we used Assembly language.
The following are some of the major reasons for writing programs in C instead of Assembly:
1) It is easier and less time consuming to write in C than in Assembly.
2) C is easier to modify and update.
3) You can use code available in function libraries.
4) C code is portable to other microcontrollers with little or no modification.
As seen in the last lab, we have used WinAVR GNU GCC Compiler for AVR.

Starting AVR Programming in C


This section provides a revision of basic syntax of C language, its data types, and functions which we will
be needing to build our logic for AVR programming.
When we create an AVR project in Code::Blocks, we get an empty main.c file with a basic template shown
in Figure 6.

Figure 6: main.c Template Code


Comments:
//Single line comment
/* Multiple line
Comment */
20
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Header files:
The #include is a preprocessor directive that instructs the compiler to find the file in the < > brackets and
tack it on at the head of the file you are about to compile. The io.h provides appropriate I/O definitions for
the device we are using, and the delay.h provides the definitions for the delay function.
Statements control the program flow and consist of keywords, expressions, and other statements. A
semicolon ends a statement.
main ( ): All C programs contain the main( ) function that contains the code and is first run when the
program begins. main (void) means the function doesn’t take any input. ‘int main’ means that the function
needs to return some integer at the end of the execution and we do so by returning 0 at the end of the
program.
While Loop: It is a control flow statement that allows code to be executed repeatedly based on a given
Boolean condition. The while loop can be thought of as a repeating if statement. The while(1) will run the
loop forever because ‘1’ is the definition of true (false is defined as 0).
Data Types: A good understanding of C data types for the AVR can help programmers to create smaller
hex files. In declaring variables, we must pay careful attention to the size of the data type and data range,
refer to the Table 1.
Remember that C compilers use the signed char as the default unless we put the keyword unsigned in front
of the char. In many situations, such as setting a counter value, where there is no need for signed data, we
should use the unsigned char instead of the signed char. Using the int instead of the unsigned char leads to
the need for more memory space.

Table 1: Data types widely used by C compilers

Declaring (Creating) Variables: type variableName = value;


Where type is one of C types (such as int), and variableName is the name of the variable (such as x or
myName). The equal sign is used to assign a value to the variable.
Delay functions: One way of generating time delay is to use predefined functions such as _delay_ms( )
and _delay_us( ) defined in delay.h in WinAVR For this, we need to include the header file delay.h For
example: #include <utils/delay.h>
Constants: Data that cannot be changed by the program. By convention, constants are named in capital
letters. These are defined at the start or in header files, then can be used anywhere in the code.
# define PI 3.1415926

21
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

AVR I/O Programming in C


All port registers of the AVR are both byte accessible and bit accessible. Let’s first discuss and practice the
byte-size I/O.
Byte size I/O
To access a PORT register as a byte, we use the PORTx label where x indicates the name of the port. The
data direction registers are accessed using DDRx to indicate the data direction of port x. To access a PIN
register as a byte, we use the PINx label where x indicates the name of the port.
Writing C-Code for Giving Output from ATmega328P Pins
Considering the above discussion, let’s check how we can output digital high or low signal from
ATmega328P I/O pins.
Example 1-Output through I/O Port to drive LEDs:
Consider the port D which has 8 I/O pins. Let’s set the first 4 pins (D0, D1, D2 and D3) as output pins. This
is done by first setting the DDR register of port D as 0b00001111.
DDRD= 0b00001111 (or, DDRD = 0X0F)
Once the port pins are set as output pins, now we can output logic high or low at these pins. Here, we will
set the Port register’s corresponding last 4-bits high.
PORTD= 0b00001111
Similarly, to set the alternate bits (D0 and D2) low, we can use;
PORTD = 0b00001010 (or, PORTD = 0X0A in Hex)
The complete code is shown in Figure with corresponding connections. Note that, the DDR registers are
set outside the while loop in the main function once.

Figure 7: Code and Connections for Example 1

22
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Writing C-Code for Taking Input to ATmega328P Pins


Considering the port B which has 8 pins. Let’s set the pin PB0, PB1, PB2 and PB3 of port B as input pins.
To take input, we need to set the DDRB port first and then we can use the PINB for reading from the port
B pins.
DDRB= 0X00 // Setting all pins as input pins
temp = PINB //reading from PINB and saving it in temp variable
Example 2 – Taking Input through I/O Ports from Switches:

Figure 8: Code and Connections for Example 2


Bit-wise Logic Operations in C for I/O Bit Manipulation
One of the most important and powerful features of the C language is its ability to perform bit manipulation.
You might be familiar with the logical operators AND (&&), OR (||), and NOT (!), but might be less familiar
with the bit-wise operators AND (&), OR (|), EX-OR (^), inverter (~), shift right (>>), and shift left (<<).
These bit-wise operators are widely used in software engineering for embedded systems and
microcontroller-based system design and interfacing.
Masking for Bit Size I/O
We use these bit-wise logical operations to access a single bit of a given register without disturbing the rest
of the byte. In this section you will see how to mask a bit of a byte.
Initially, the output port PORTD is set as;
PORTD = 0b 00000 0000; // All bits are clear
To set the 5th bit high without disturbing the other bits, we can perform bit-wise OR | operation of PORTD
register’s previous value with 0b00010000.
PORTD = PORTD | 0b00010000; // To set the 5th bit (bit # 4) only
Similarly, if PORTD is initially set as 0b11111111, and we just want to clear the 4 th bit (Bit #3) then we
can perform a bit-wise AND operation.
PORTD= PORTD & 0b111110111;
Irrespective of the value of a bit, the AND operation of the bit with 1 results in the previous value of the bit
i.e. it remains unchanged. Whereas, AND operation of a bit with 0 clears the bit.
23
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Similarly, the OR operation of a bit with 1, results in setting the bit high. However, OR operation with 0
leaves the bit unchanged.
Table 2: Bit-wise Logical Operators in C

To invert all bits, the bit-wise NOT (~) operator can be used.
PORTD = ~PORTD;
Example 3 – Using logical operators for bit-wise I/O from ATmega328P ports
The following examples get the status of bit 3 of Port D and send it to the bit 0 of port D continuously.

Figure 9: Code and Connections for Example 3


Compound Assignment Operators
To reduce coding (typing) we can use compound statements for bit-wise operators in C.
Table 3: Compound Assignment Operators

24
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

Example 4 – Using compound assignment operators for bit-wise I/O from ATmega328P ports
The Example 3 is re-written using the compound bit-wise operators. Observe the difference. Note that the
compound assignment operators ‘|=’ don’t have a space ‘| =’ in between.

Figure 10: Code and Connections for Example 4


Shift Operation for Bit-Manipulation:
To do bit-wise I/O operation in C, we need numbers like 0b00100000 in which there are seven 0s and one
1. Only the position of the one varies in different programs. To leave the generation of ones and zeros to
the compiler and improve the code clarity, we use shift operations. For example, instead of writing
“0b00100000” we can write “0b00000001 << 5” or we can write simply “1<<5”. Sometimes we need
numbers like 0b11101111. To generate such a number, we do the shifting first and then invert it. For
example, to generate 0b11101111 we can write ~(1<<4).

LAB TASKS
From Task 2 to 5, create AVR projects and test them on ATmega328P. Feel free to use different conditional
statements, loops, switch-case structure to complete the C-programming related tasks.
TASK 1: Explain what makes the Blinky.c blink?
The code we tested in Lab 01 is given below. Explain what makes this Blinky.c blink?

//Blinky.c from Lab01


#include <avr/io.h>
#define BLINK_DELAY_MS 1000
#include <util/delay.h>
int main (void)
{
DDRB |= 0B100000;
while(1) {
PORTB |= 0B100000;
_delay_ms(BLINK_DELAY_MS);
PORTB &= ~ 0B100000;
_delay_ms(BLINK_DELAY_MS); }
}

25
Embedded Systems Lab Lab 02 AVR Port Programming for Digital Input & Output
NED University of Engineering & Technology Electrical Engineering Department

TASK 2: To check and indicate the status of a sensor using the specified ports and bits of
ATmega328P
A door sensor (here, assume the switch) is connected to pin 1 of Port B, and an LED is connected to pin 5
of Port C. Write an AVR C program to monitor the door sensor and, when it opens, turn on the LED.
TASK 3: To use the general I/O pins of ATmega328P as input or output pin based on the
given condition
Write an AVR C program to monitor bit 7 of Port B. If it is 1, make bit 4 of Port B input; otherwise, change
pin 4 of Port B to output.
TASK 4: To control the specified pins of a given port without disturbing the rest of the pins
Write an AVR C program to control a set of 8 LEDs connected to port D such that the first 4 LED glow
when input from a switch is high, and remain off when the input from switch is low. The remaining 4 LED
toggle continuously without disturbing the rest of the pins of port D.
TASK 5: To control the output based on combination of 2 input pins
Write an AVR C program to read pins 0 and 1 of Port B and update the LEDs at pin 0, 1 & 2 of Port D
according to the following logic. You can use switch-case structure.
Input Port B [1:0] Status Output Port D [2:0] Status
0b 00 0b 000
0b 01 0b 011
0b 10 0b 101
0b 11 0b 111

26
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 03
OBJECTIVE:
To program the ATmega328P for reading analog input through its Analog-to-Digital Converter ADC
module

LAB OUTCOMES:
By the end of the lab, you would be able to:
1) Identify the AVR ATmega328P pins associated with the ADC module
2) Identify the purpose of different bits of ADC registers and utilize them for their set purposes like
setting reference voltage, selecting source of analog input, and indicating start / end of conversion
3) Take analog input through the ADC pins and indicate its digital equivalent
4) Build required logics for reading analog input in pure C-language codes and test them on
ATmega328P microcontroller
5) Verify the analog to digital conversion ADC of microcontroller pins by testing the digital output
through external DAC (digital-to-analog) R-2R circuit / DAC0808 IC

BACKGROUND:
Digital computers use binary (discrete) values, but in the physical world the signals are analog (continuous).
Most physical variables are analog in nature and can take on any value within a continuous range of values.
Temperature, pressure, humidity, and velocity are a few examples of physical quantities that we deal with
every day. For acquisition of analog signals, we need Analog-to-Digital (ADC) module for conversion.
Microcontrollers are therefore generally featured with ADC module. In this lab, we will explore
ATmega328P ADC input channels that enable us to capture analog signals.
Basics of Analog-to-Digital Conversion (ADC)
An analog-to-digital converter takes an analog input voltage and after a certain amount of time produces a
digital output code which represents the analog input. ADC involves the following steps:
● Sampling: Sampling is the processes of converting
continuous- time analog signal into a discrete-time
signal by taking the “samples” at discrete-time
intervals. Sampling analog signals makes them
discrete in time but still continuous valued.
Sampling frequency determines the intervals at
which samples are taken. Nyquist criterion requires
that the sampling frequency be at least twice the
highest frequency contained in the signal.
● Quantization: Quantization is the process of
mapping continuous infinite values to a smaller set
of discrete finite values. The quantization step size
is the smallest possible difference in amplitude
between samples.
● Encoding: After quantization, each quantization
Figure 1: Analog-to-Digital Conversion
level is assigned a unique binary code.

27
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Relation between the Number of Bits, Resolution and Reference Voltage


The quantization levels are dependent on the number of bits available or required for representing the digital
output. ‘n’ is the number of bits, then the quantization levels are 2𝑛 (0 to 2𝑛−1 ). Consequently, the ADC
has n-bit resolution. Higher-resolution ADCs provide a smaller step size, where step size is the smallest
change that can be discerned by an ADC. The resolution is dependent on reference voltage as well. The
number of bits and reference voltage decide the step-size. This is related as:
𝑉𝑟𝑒𝑓
𝑆𝑡𝑒𝑝 𝑠𝑖𝑧𝑒 =
2𝑛
Table 1: Reference Voltage Relation with Resolution for 8-bit and 10-bit ADC
Reference Voltage Step-size for 8-bit ADC Step-size for 10-bit ADC
5V 5/256 = 19.53 mV 5/1024= 4.88 mV
3.3 V 3.3/256 = 12.89 mV 3.3/ 1024 = 3.22 mV
1.1 V 1.1/256 = 4.297 mV 1.1/1024 = 1.074 mV

● Range of analog input voltage for ADC: 0 to Vref.

In an 8-bit ADC we have an 8-bit digital data output of


D0–D7, while in the 10-bit ADC the data output is D0–
D9.

The obtained digital output 𝐷𝑜𝑢𝑡 is related to the input


analog voltage 𝑉𝑖𝑛 as:

𝑉𝑖𝑛
𝐷𝑜𝑢𝑡 =
𝑆𝑡𝑒𝑝 − 𝑠𝑖𝑧𝑒 Figure 2: 8-bit ADC Representation

𝐷𝑜𝑢𝑡 is the decimal equivalent of n-bit binary result.

ADC (Analog-to-Digital Conversion) Module of ATmega328P


ATmega328P microcontroller ADC module capable of converting an analog voltage into a 10-bit number
from 0 to 1023 or an 8-bit number from 0 to 255. There are 6 ADC input channels on the chip as shown in
Figure 3 (pin # 23 to 28 = ADC0 to ADC5). The input to the module can be selected from any one of the 6
inputs on the chip i.e., one channel can be converted at a time. The inputs to the ADC module appear on
the Arduino board as connections A0 through A5.
In Figure 3, you can see a few pins, other than ADC0 to ADC5, marked with Green color, representing
their connection with analog related circuitry.
The function of different pins for utilization of the ADC module is explained with their associated registers
in the following sections.

28
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Figure 3: ATmega328P pin diagram


Pins for ATmega328P ADC
Table 2: ATmega328P pins related to the analog circuitry
ADC Module –
Pin # Port Function
Pin Names
23 PC0 ADC0 ADC Input Channel 0
24 PC1 ADC1 ADC Input Channel 1
25 PC2 ADC2 ADC Input Channel 2
26 PC3 ADC3 ADC Input Channel 3
27 PC4 ADC4 ADC Input Channel 4
28 PC5 ADC5 ADC Input Channel 5
21 - AREF External voltage supply for setting reference voltage.
By connecting a capacitor between the AREF pin and
GND, reference voltage becomes more stable and
increases the precision of ADC
20 - AVCC Provides the supply for analog ADC circuitry.
12 PD6 AIN0 Analog Comparator Positive & Negative Input- AIN0 &
13 PD7 AIN1 AIN1. These pins are associated with analog comparator
module. These are not necessarily needed for ADC.

Registers for AVR ADC Programming


Five major registers are associated with the ADC module for interfacing it. Let’s examine each one-by-
one. The description given here is quite brief. You can refer to the datasheet for further details.
● ADCH (high data)
● ADCL (low data)
● ADCSRA and ADCSRB (ADC Control and Status Registers A & B)
● ADMUX (ADC multiplexer selection register)
● DIDR0

29
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

The name of register is written in capital letters. Each register is 8-bit wide. Indexing is done to show
different bits, for example XYZ [2:0] means the least significant 3 bits of the register XYZ (XYZ2, XYZ1
& XYZ0). XYZ[2:0] = 5 would mean (XYZ2 =1, XYZ1= 0 and XYZ0 =1 since 5 = 0b101).
1) ADC Data Register Low and High Byte
After the A/D conversion is complete, the result is stored in registers ADCL (A/D Result Low Byte) and
ACDH (A/D Result High Byte). For 10-bit ADC result, the eight bits sit in one 8-bit register and the
remaining two bits are provided in the other register, with six bits being unused. The result can be left or
right adjusted as shown below. If only eight bits of resolution are needed, the ADC value is left-justified
and the high-order byte are read through ADCH.

Figure 4: 10-bit ADC result adjustment in ADCH and ADCL


2) ADMUX (ADC Multiplexer Selection Register)
It is an 8-bit register with bits illustrated below.

● Reference Selector Bits (REFS [1:0]) The ADMUX [7:6] bits select the voltage reference for the
ADC.
Table 3: Function of the reference selector bits
ADMUX[7:6] Reference High
Description
= REFS[1:0] Voltage Selection
00 AREF Voltage provided at AREF pin externally (Internal Vref turned OFF)
AVCC with external capacitor at AREF pin. Note: Arduino already has
01 AVCC
capacitor placed on line
Internal 1.1 V reference fixed regardless of VCC. Note: Arduino already
11 Internal 1.1V
has capacitor placed on line

Figure 5: Reference voltage selection


● ADC Left Adjust Result (ADLAR) ADMUX [5] is called ADLAR. The ADLAR bit affects the
presentation of the ADC conversion result in the ADC data register.
Table 4: ADLAR bit values for result adjustment
ADLAR Conversion Result
0 Right adjusted for 10-bit result
1 Left adjusted for 8-bit result

30
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

● Analog Channel Selection (MUX[3:0]) The 4 bits ADMUX[3:0] serve as selector for the
multiplexer that selects one of the 6 analog input channels to be connected to ADC.
Table 5: Input Channel Selection for ADC
MUX [3:0] Input Channel
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
1000 Temperature Measurement
Interesting Fact: The Atmega328P has an internal temperature sensor. Refer to the datasheet and read
about it. The output of the temperature sensor can be selected as one of the inputs for ADC module as
shown in the description of ADMUX Channel Selection bits.
3) ADCSRA (ADC Control and Status Register A)

Table 6: Function of ADCSRA Register Bits


Fields Full-Form ADSCRA Bit No. Function for Different Values
1: ADC is enabled
ADEN ADC Enable ADSCRA[7]
0: ADC is turned off
1: To start each conversion in Single Conversion
ADC Start
ADSC ADSCRA[6] mode
Conversion
Returns to 0 when conversion is complete.
It is set to 1 for enabling auto-triggering of ADC
ADC Auto
through the selected trigger signal. The ADC tested in
ADATE Trigger ADSCRA[5]
this lab is not external interrupt-driven. It is set 0 for
Enable
single-conversion mode.
ADC This bit is set when an ADC conversion completes
ADIF Interrupt ADSCRA[4] and the data registers are updated. It is used to
Flag identify completion of conversion.
ADC
Not needed for now. It is used to activate ADC
ADIE Interrupt ADSCRA[3]
conversion complete interrupt.
Enable
These bits determine the division factor between the
system clock frequency and the input clock to the
ADC. 50kHz to 200kHz is acceptable for ADC
circuitry
System clock frequency for Arduino UNO is 16MHz
ADC Pre- (Crystal oscillator connected with UNO).
ADPS ADSCRA[2:0]
scalar Select ADPS[2:0]- Factor ADPS[2:0]- Factor
000 – 1 100 – 16
001 – 2 101 – 32
010 – 4 110 – 64
011 – 8 111 – 128

31
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

4) DIDR0 (Digital Input Disable Register 0)


When an analog signal is applied to the ADC5...0 pin and the digital input from this pin is not needed, this
bit should be written logic one to reduce power consumption in the digital input buffer. DIDR0 = 0x3F

5) ADCSRB
This is not needed for now. It has 2 important fields: (ACME-Analog Comparator Multiplexer Enable
and ADTS[2:0]- Auto Trigger Source Selection).

Steps for ATmega328P ADC Programming and C-Code


To program the A/D converter of the AVR, the following
Relevant Register Values or C-Code
steps must be taken:
1. Make the pin for the selected ADC channel an input pin. DDRC=0b00000000//assume PortC
2. Turn on the ADC module of the AVR because it is
disabled upon power-on reset to save power.
ADCSRA =0b10000111
3. Select the conversion speed. We use registers ADPS2:0
to select the conversion speed.
ADMUX = 0b01100000
4. Select voltage reference and ADC input channels. Vref is AVCC (REFS = 01)
Left Adjusted (ADLAR=1)
ADC0 Input Channel (MUX=0000)
5. Activate the start conversion bit by writing a one to the ADCSRA | = (1<<6)
ADSC bit of ADCSRA. Or, 1<< ADSC
6. Wait for the conversion to be completed by checking the (ADSCRA & (0b00010000))
ADIF bit in the ADCSRA register. Equal to 0 as long as conversion takes place
due to ADIF bit (0)
(ADSCRA & (0b00010000))
NOT equal to 0 when conversion is
7. After the ADIF bit has gone HIGH, read the ADCL and complete due to ADIF bit (1)
ADCH registers to get the digital data output. Read:
For 8-bit result only, x=ADCH;
Notice that you have to read ADCL before ADCH; Where, x is unsigned char
Or to forward at output, PORTB=ADCH
otherwise, the result will not be valid.
For 10-bit result, x=ADC
Where, x is unsigned int
Or, PORTB=ADCL; PORTD=ADCH
8. If you want to read the selected channel again, go back to step 5.
If you want to select another Vref source or input channel, go back to step 4

NOTE: The ADC has two different operating modes. In single conversion mode, each conversion will
be initiated by the user. In free running mode, the ADC is constantly sampling and updating the ADC
Data Registers. When a conversion is complete, the result is written to the ADC data registers, and ADIF
is set. In single conversion mode, ADSC is cleared simultaneously. The software may then set ADSC
again to start a new conversion. In free running mode, a new conversion will be started immediately after
32
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

the conversion completes while ADSC remains high. For further description, refer to the ATmega328P
datasheet section Analog to Digital Converter.
Example # 1: Reading an Analog Voltage Set by a Potentiometer
In this example, we are taking analog input through a potentiometer. The analog input voltage can vary
from 0 to 5V. This is given at ADC0 input channel. The digital 8-bit output is taken through PORTD pins
by reading the ADCH register. An LED is used to indicate each bit of the digital output. For a reference
voltage of 5V, using 8-bit ADC, each level corresponds to 19.53 mV.

Figure 6: Code for Example 1- Reading Analog Input from ADC0 Channel

Figure 7: Connections and Output Representation for Example 1

33
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

LAB TASKS
TASK 1: To test the Example 1 on ATmega328P and verify working of ADC using an
external D/A Converter
1. Create a new AVR project and build the code given in the Example 1. Test the obtained digital output
by varying the output from potentiometer. Observe the on / off status of LEDs (used as indicators for
digital output) and verify the digital voltage by calculation for 8-bit ADC at given reference voltage.
Note that the ATmega328P has an ADC module but not a built-in DAC module so it cannot provide an
analog output through any of its pins.
2. Now, remove the LEDs and provide the 8-bit digital output to an external DAC circuit i.e., convert
the digital output to analog for verification. You can use a simple R-2R circuit of Figure 8 or use
DAC0808 IC. For DAC0808 IC, refer to its datasheet for more information. The pin diagram and
DAC circuit using this is shown in Figure 9 and 10.

Figure 8: Layout of 8-bit R-2R Ladder Circuit-DAC

Figure 9: DAC0808 (8-bit R-2R based


D/A Converter)
Figure 10: Sample Circuit for Digital to Analog Converter
Circuit using DAC0808

3. Measure the analog input given through potentiometer and the analog output reproduced by the DAC.
Compare both and verify the ADC and DAC. This is shown in Figure 11 using R-2R circuit.

34
Embedded Systems Lab Lab 03 ADC Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Figure 11: Sample Representation of Task 1 with R-2R DAC


TASK 2: To control the status of an LED based on the value of input analog voltage
Modify the previous task or, example to read 10-bit ADC value of voltage across potentiometer instead of
8-bit result. Map the obtained 10-bit result with voltage level using the step-size. Connect an LED to
indicate the voltage level. Use some conditions to build the following logic for controlling the LED status.
- If voltage is above 2.5V, the LED turns ON
- If voltage is below 2.5V, the LED red turns OFF

Note: Pay attention to the data type of variables when you calculate the input analog voltage using
the step-size and ADC result.

35
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 04
OBJECTIVE:
To utilize the USART (Universal Synchronous / Asynchronous Receiver /Transmitter) of ATmega328P for
transmitting and receiving data though asynchronous serial communication with PC

LAB OUTCOMES:
By the end of this lab, you should be able to:
1) Recognize the basics of serial communication protocol; baud-rate, stop bit, data bits, parity etc. and
the importance of required connectors (RS-232)
2) Identify the AVR ATmega328P pins associated with the USART
3) Identify the purpose of different fields of USART registers
4) Program ATmega328P for initializing the USART with given baud-rate
5) Program ATmega328P in C-language to establish serial communication with PC
6) Test and verify data (character, string, integer and float) transmission and reception for given
conditions using a Serial Terminal Emulator like TeraTerm

“The single biggest problem in communication is the illusion that it has taken place.”
– George Bernard Shaw

BACKGROUND:
Microcontrollers are provided with ability to communicate with external devices like computer, other
micro-controllers and peripherals. This communication is done through different protocols to allow
microcontrollers to send and receive data. ATmega328P is provided with USART (Universal
Synchronous/Asynchronous Transmitter/Receiver). In this lab, we will be exploring Asynchronous
Transmitter and Receiver (UART). It is not only used as a communications link to external device but
also as a debugging port to send status messages. This is one of the 3 communication options that can be
established with ATmga328P. The other two are SPI and I2C which will be explored later. Before
discussing the working of relevant pins and registers, we first need to understand the different types and
basics of communication protocols.

Figure 1: Serial Communication of Microcontroller with PC for Troubleshooting

36
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Basics of Serial Communication


Parallel Vs Serial Data Transfer: Computers transfer data in two ways: parallel and serial.

Figure 2: Serial and Parallel Data Transfer Representation


In parallel data transfer, the data is sent one byte (or multiple bits) at a time. For this multiple wires are
needed and this is suitable for a short-distance like printers. In serial communication, the data is sent one
bit at a time. It needs lesser number of wires. It is suitable for longer distance communication and is cheaper.
Synchronous vs Asynchronous Communication: Serial data communication can be, asynchronous or
synchronous. The synchronous method transfers a block of data (characters) at a time, whereas the
asynchronous method transfers a single byte at a time. In Synchronous transmission a common clock is
shared by the transmitter and receiver to achieve synchronization while data transmission. In asynchronous
interface, it does not have any separate clock signal. Only the data is sent on the lines and the transmitter
must send the data at an agreed upon rate and in a defined manner.
Universal Synchronous and Asynchronous serial Receiver and Transmitter (USART) is a configurable
peripheral of ATmega328p which supports both Synchronous (SPI) and Asynchronous (Serial)
communication protocols. In our case we will be dealing only with the asynchronous communication.
Asynchronous Communication Protocol
Baud Rate – Data Transfer Rate: The rate of data transfer in serial data communication is stated in bps
(bits per second). Another widely used terminology for bps is baud rate. In the context of microcontroller
USART programming, we will be using the terms bps and baud interchangeably. The maximum data
transfer rate is limited by the hardware ports but the transmitter and receiver must agree on the same baud
rate.
Data Framing: In data framing for asynchronous communications, the data, such as ASCII characters,
are packed between a start bit and a stop bit. The start bit is always one bit, but the stop bit can be one or
two bits. In modern PCs, however, the use of one stop bit is standard. The start bit is always a 0 (low),
and the stop bit(s) is 1 (high). For example, look at Figure in which the ASCII character “A” (8-bit
binary 0100 0001) is framed between the start bit and a single stop bit. Notice that the LSB is sent out
first. There are a total of 10 bits for the character: 8 data bits for the ASCII code, and 1 bit each for the
start and stop bits.

Figure 3: Framing ASCII ‘A’ (41H) with a stop-bit (1) and a start bit (0)

37
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Parity Bit: UART chips allow programming of the parity bit for odd-, even-, and no-parity options. It is a
single bit added to the data frame to maintain data integrity.

ATmega328P Serial Port Pins and Connection with PC


ATmega328P has one serial port. Pin 2 and pin 3 of ATmega328P serve as USART0 transmitter (TxD)
and receiver (RxD) pins. Arduino UNO pin 0 and 1 are therefore marked as TX and RX. These are the
same pins on the chip as I/O ports PD1 and PD0. This means that applications that use the USART0 cannot
also use these two bits in Port D. It is not necessary to set any bits in the DDRD register in order to use the
USART0.
If LEDs placed on the RX and TX pins will flash, they indicate the transmission of data.

Figure 4: Connection of ATmega328P RXD and TXD pins with Arduino UNO for UART

Figure 5: Placement of RX and TX pins and corresponding LEDs with ATmega328P and ATmega16 on
Arduino UNO board
To establish communication between microcontroller (USART pins) with PC, the PC must have a
communication port to support serial data transfer. These are called COM ports. A COM port is simply an
I/O interface that enables the connection of a serial device to a computer. COM ports are also referred to as
serial ports. They are asynchronous interfaces that can transmit one bit of data at a time when connected to
a serial device.

Figure 6: RS-232 Serial Port (DB9)


The AVR serial port can be connected to the COM port of a PC for serial communication. However, USB
interfaces have largely replaced the RS232 serial ports seen in the past as a faster way of performing serial

38
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

data transmission. In the absence of a COM port, a COM-to-USB converter module is needed. You can
read more about it here.
Luckily, the Atmega16U2 incorporated on the UNO (R3) board acts as a USB-to-serial converter for serial
communication using USB com drivers. On PC, a software applications is used with it to send data to or
display the received data from the board.

ATmega328P Serial Port Registers


1) UDR0 – USART Data I/O Register
This register is used to hold data to be sent or received.
● The USART Transmit Data Buffer Register and USART Receive Data Buffer Registers share the same
I/O address referred to as USART Data Register or UDR0.
● For data transmission, TXB will be the destination for data written to the UDR0 Register location.
● For data reception, reading the UDR0 Register location will return the contents of RXB.

2) UCSR0A – USART0 Control and Status Register A


UCSR0 [7] [6] [5] [4] [3] [2] [1] [0]
A RXC0 TXC0 UDRE0 FE0 DOR0 PE0 U2X0 MPCM0
Bits Function
RXC0 USART Receive Complete
Indicates receive buffer register (RXB) status.
1: Unread data present in receive buffer
0: Receive buffer is empty
TXC0 USART Transmit Complete
1: Entire frame in transmit shift register has been transmitted, no new data
available in transmit data buffer register (TXB)
UDRE0 USART Data Register Empty
1: Transmit data buffer register is ready to receive new data
0: TXB is not empty. Don’t write to UDR if UDRE0 is 0
FE0 Frame Error
1: Frame error occurred in receiving next character in receive buffer
Frame error is detected if first stop bit of character in RXB is 0
DOR0 Data Over-Run
1: Indicates data over-run
Data over-run occurs if RXB and receive shift-register are full and new start bit is detected.
PE0 USART Parity Error
1: Indicates parity error in the receive buffer if Parity Checking UPM01 is enabled.
U2X0 Double the USART Transmission Speed
1: It doubles the transfer rate for asynchronous operation (baud rate divisor
becomes 8 instead of 16)
MPCM0 Multi-processor Communication Mode
Enables the Multi-processor Communication mode. Transmitter is unaffected by it.
The default value of UCSR0A to 0x20 = 0b 0010 0000

39
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

3) UCSR0B – USART0 Control and Status Register B


[7] [6] [5] [4] [3] [2] [1] [0]
UCSR0B
RXCIE0 TXCIE0 UDRIE0 RXEN0 TXEN0 UCSZ02 RXB80 TXB80
Bits Function
RXCIE0 RX Complete Interrupt Enable
1: Set 1 to enable the interrupt on the RXC flag in UCSR0A
TXCIE0 TX Complete Interrupt Enable
1: Set to enable the interrupt on the TXC flag in UCSR0A
UDRIE0 USART Data Register Empty Interrupt Enable
1: Set to one enables interrupt on the UDRE0
RXEN0 Receiver Enable
1: Enables the USART Receiver
0: Disables the Receiver. Flushes the RXB.
TXEN0 Transmitter Enable
1: Enables the USART Transmitter
0: Disables the Transmitter; effective once transmission is complete.
UCSZ02 Character Size
The UCSZn2 bits combined with the UCSZn1:0 bit in UCSR0C sets the number of data
bits (Character SiZe) in a frame the Receiver and Transmitter use.
RXB80 Receive Data Bit 8
It is the ninth data bit of the received character when operating with serial frames with nine
data bits. Must be read before reading the low bits from UDR0.
TXB80 Transmit Data Bit 8
It is the ninth data bit in the character to be transmitted when operating with serial frames
with nine data bits. Must be written before writing the low bits to UDR0.
RXEN0, TXEN0 and UCSZ02 are most important here for enabling the receiver, and transmitted and to
set the character size. The interrupt related bits are not needed now. Its default value is 0x00.
4) UCSR0C – USART0 Control and Status Register C
[7] [6] [5] [4] [3] [2] [1] [0]
UCSR0C
UMSEL01 UMSEL00 UPM01 UPM00 USBS0 UCSZ01 UCSZ00 UCPOL0
Bits Function
UMSEL0 USART Mode Select Bits (UMSEL01-00)
1 00: Asynchronous USART
UMSEL0 01: Synchronous USART
0
UPM01 USART Parity Mode (UPM01-00)
These enable and set type of parity generation and check. If enabled, the Transmitter will
automatically generate and send the parity of the transmitted data bits within each frame.
The Receiver will generate a parity value for the incoming data and compare it to the UPM0
UPM00 setting. If a mismatch is detected, the UPE0 Flag in UCSR0A will be set.
00: Disabled
10: Enabled, Even Parity
11: Enabled, Odd Parity
USBS0 USART Stop Select Bit
Selects number of stop bits to be inserted by the Transmitter.
0: 1-bit Stop bit
1: 2-bit Stop bits

40
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

UCSZ01 Character Size (UCSZ01:00)


The UCSZ01:00 bits combined with the UCSZ02 bit in UCSR0B sets the number of data
bits (Character SiZe) in a frame the Receiver and Transmitter use.

UCSZ00 [UCSZ02,USCZ01,UCSZ00] Character Size


000 5-bit
001 6-bit
010 7-bit
011 8-bit
111 9-bit
UCPOL0 Clock Polarity
0: The bit is cleared when asynchronous mode is used.
In synchronous mode, it is useful for setting relation between data and clock.
Correct initialization of all the UCSR0C bits is important in setting communication protocols. Its default
value is 0x06 = 0b000 0110.
5) UBRR0 – USART0 Baud Rate Register
It is used to set baud-rate by specifying the pre-scalar in its 12 bits.

UBRR[15:12] The 4 bits, reserved, are set to 0. The remaining 12 bits UBRR[11:0] contain the USART0
baud rate (pre-scalar). The UBRR0H contains the four most significant bits, and the UBRR0L contains the
eight least significant bits of the USART0 baud rate.
For required baud rate ‘BAUD’, and oscillator frequency fosc, the value for UBRR0 is calculated by;
𝑓𝑂𝑆𝐶
𝑈𝐵𝑅𝑅0 = −1
16 × 𝐵𝐴𝑈𝐷
For a 16MHz clock, the required values of UBRR0 register are given in the Table below for different baud-
rates. Note: U2X0 is set 0 here. The baud-rates can be doubled by setting U2X0 high for same UBRR0
values. The formula can be applied for verification.
Table 1: UBRR0 Values for Different Baud Rates
Baud Rate (bps) UBRR0
2400 416 = 0x01A0
4800 207 = 0x00CF
9600 103= 0x0067
14400 68 = 0x0044
19200 51 =0x0033
Serial Terminal Emulator – TeraTerm
COM Port (communication port) is the original, yet still common, name of the serial port interface on PC-
compatible computers. It can refer not only to physical ports, but also to emulated ports.
Serial terminal emulators are software applications that replicate physical COM ports. The virtual serial
ports are fully compatible with operating systems and applications and are treated in the same way as a real
port. These are used for the serial communication between the host computer and an embedded system
41
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

(Target). It is mainly used as a user interface for debugging embedded system. It is also used for sending
commands, displaying result, loading firmware, logging result, etc.
Tera Term and PuTTY are famous terminal emulator applications. In this lab, we can use Tera Term. It is
an open-source, free, software implemented terminal emulator (communications) program.
1) Download Tera-Term using: https://filehippo.com/download_tera-term/
2) Type Tera Term in Windows search to open it. You will be able to select Serial once you connect
your device to PC USB port (connect Arduino UNO). The Serial and Port options will be enabled.

Figure 7: Tera Term Connection


3) Go to Setup >> Serial port… It allows you to select Baud Rate, Stop Bit, Data Bits, and Parity etc.

Figure 8: Tera Term Serial Port Settings


4) For writing to Serial Terminal window (send character from PC to Microcontroller), turn on echo. Go
to Setup >> Terminal and select Local echo.

Figure 9: Tera Term Terminal Setup


5) Save the settings for later use. Select Setup > Save setup.

42
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

ATmega328P Serial Port Programming in C


Programming for USART Initialization
The USART has to be initialized before any communication can take place.

The initialization process consists of the following steps. Relevant Register Values (example) & C-Code

1. Setting-up as Transmitter and / or Receiver


Enable the TxD and (or) RxD pins using TXEN0 UCSR0B=0b00011000 or,
UCSR0B =(1<<TXEN)| (1<<RXEN)
and (or) RXEN0 bits of UCSR0B.
2. Setting-up Frame Rate
Load UCSR0C to indicate asynchronous mode with UCSR0C=0x06 or,
UCSR0C=0b00000110
8-bit data frame, no parity, and one stop bit
UBRR0 = 0x67
3. Set the baud-rate using UBRR. For baud-rate of 9600 bps at 16MHz crystal
frequency at U2X0 =0.
 Subroutine for USART initialization
For ease, we create a subroutine for USART initialization to avoid repeating these lines of code again and
again.
void usart_init (void)
{
UBRR0=0x67; //set pre-scalar to configure baud rate (9600)
UCSR0A &= ~ (1 << U2X0); //Single Speed U2X - 0 (can be set to 1)
UCSR0B = (1 << RXEN0) | (1 << TXEN0); // enable transmitter and receiver
// Async mode, parity mode = disabled, 1 stop bit, character size = 8 bit
clock polarity = 0 for async communication
UCSR0C = (0<<USBS0) | (1 << UCSZ01) | (1 << UCSZ00);
}

Programming for Data Transmission

To program the USART as Transmitter, follow the steps: Relevant Register Values or C-Code

1. Initialize the USART. Call usart_init( );


while (!(UCSR0A & (1<<UDRE0)))
2. Monitor the UDRE bit of UCSR0A to make sure { };
UDR is ready to accept byte to transmit. An empty while loop that waits to check UDR is
empty (indicated by UDRE bit).
3. Write the character to be transmitted to the UDR. UDR0=ch;
Where, ch is an unsigned char for example ‘A’.
4. Wait for complete frame transmission. while(!(UCSR0A &(1 << TXC0))){};
5. To transmit the next character, go to Step 2.
 Subroutine for transmitting character
void usart_putChar(unsigned char data)
{
while (!(UCSR0A & (1 << UDRE0))) {}; //wait for data register to be empty
UDR0 = data; //write data
while (!(UCSR0A & (1 << TXC0)) ) {}; //wait for complete frame transmission
}
43
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Programming for Data Reception

To program the USART as Receiver, follow the steps: Relevant Register Values or C-Code

1. Initialize the USART. Call usart_init( );


While (! (UCSR0A & (1<<RXC0))) ;
2. Monitor the RXC flag bit of the UCSR0A register to An empty while loop that waits for data to be
see if an entire character has been received yet. received. Loop ends when RXC bit turns high as
the given condition is not equal to 0.
3. When RXC is high, read the UDR as it has received ch=UDR;
the byte. Where, ch is an unsigned char type variable to
store the received character.
4. To receive the next character, go to Step 4.
 Function for receiving character
char usart_getChar()
{
char data;
while ( !(UCSR0A & (1 << RXC0))) {}; // wait for data to receive
data = UDR0;
return data;
}

EXAMPLES
The tested asynchronous communication is polling based and not interrupt based.
Example # 1: Transmitting a Character from ATmega328P to PC through UART
The following code transmits ‘A’ repeatedly with a delay of 1 sec. Note that only one character is sent at a
time. The following example uses usart_init( ) and usart_putChar( ) from the listed 3 sub-routines therefore,
these sub-routines must be defined in the main.c file.

Figure 10: Code for Example#1


See the character ‘A’ is written in quotes. You can write its ASCII code (65). In that case, don’t use ‘ ’ as
we want to transmit a single character ‘A’ through the ASCII code and not the integer 65 (which are 2
characters).
Now, you are able to do Task 1 and Task 2.

44
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Example # 2: Transmitting and Receiving Strings, Integers and Floats


For transmitting a string of characters (instead of a single character), we can write a C subroutine that
transmits one character at a time using the previously described usart_putChar( ). Similarly, to receive a
string of characters, we can makes use of a buffer to hold the received characters in the form of a string.
Look at the following subroutines.
 Subroutines for receiving and transmitting strings
//subroutine for transmitting a character string
void usart_putString(char* StringPtr)
{
while(*StringPtr != 0x00)
{
usart_putChar((unsigned char)*StringPtr);
StringPtr++;
}
}

//subroutine for receiving a character string


void usart_getString(volatile char buffer[], uint8_t len)
Type
{ Casting
uint8_t
Converting i =0; into another is known as type casting or, type-conversion. To transmit integer or
one datatype
for( i = 0; i < len; i++)
float data through USART, we first need to convert them into string. There are different approaches to do
{
so, one simple technique is shown here.
buffer[i] = usart_getChar();//save the received character
 Integer
} to String:
buffer[i]=0; //making the received string null terminated, last byte 0
The itoa(num,buffer,10) function coverts the integer num into a null-terminated character string. The
string
} is placed in the buffer passed, which must be large enough to hold the output. The last input shows
number format 10 for decimal. Include <stdlib.h> for itoa( ).
 Float to String:
The dtostrf (val,width,prec,s) function converts the double value passed in val into an ASCII
representation that will be stored under s. Conversion is done in the format '[-]d.ddd'. The minimum field
width of the output string (including the possible '.' and the possible sign for negative values) is given in
width, and prec determines the number of digits after the decimal sign. The dtostrf() function returns the
pointer to the converted string s.

The example code given here shows transmission of strings, float and integers by utilizing the subroutines
and functions discussed above. You can observe that a string is received from PC to AVR too.

45
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

Figure 11: Code for Example 2-Transmitting Strings, Float and Integer Data Types
Header File usart.h
For ease, you can include the header and source files (usart.h and usart.c) provided with the manual. Utilize
its simple functions for transmitting and receiving data or use the sub-routines discussed above in your code
to complete the given lab tasks.

LAB TASKS
TASK 1: To test Example 1 for transmitting a character serially at baud-rate of 9600 with 1
stop bit using ATmega328P USART
Test the Example 1 code using Arduino UNO. After building the given code, program the ATmega328P.
Now, disconnect and reconnect Arduino UNO with PC port. Open Serial Terminal Emulator (Tera Term).
Make connection with the required settings and observe the serial terminal. You should see the data
transmitted by AVR (received by the PC) on serial terminal.

 Is there any impact if you select a different baud rate in Tera Term without changing the baud rate
initialized in the ATmega328P code? Are you able to correctly transmit the characters when
microcontroller and PC work at different baud rate?
________________________________________
 Add two more lines to the code and comment on the result.
usart_putChar(65)
usart_putChar(‘65’)

46
Embedded Systems Lab Lab 04 Serial Port Programming of AVR
NED University of Engineering & Technology Electrical Engineering Department

TASK 2: To program ATmega328P for controlling the status of LED based on the received
character
Modify the previous code to make the microcontroller receive a character sent by PC. If the received
character is ‘A’, turn on the on board LED otherwise turn it off. The data should be received at baud-rate
of 14400 with 1 stop bit.
TASK 3: To transmit the analog voltage across a potentiometer read by the ATmega328P
ADC to PC
Extend the Task 2 of Lab 03 where you used ADC module to measure the voltage across potentiometer.
Send (transmit) the following through USART of the microcontroller to the PC serial terminal.
1) The 10-bit ADC output (in range of 0 to 1023)
2) The analog voltage across potentiometer in Volts

Pay attention to the data-type. You may use any suitable baud rate of your choice.
Vary the voltage and observe the values. Verify the analog voltage reading by ADC module and
transmission through the USART by comparing voltmeter reading and values displayed by Tera Term.
Sample Output:

**********10-bit ADC************
**********5V Reference Voltage****
*****Voltage across Potentiometer***

ADC Value: 307


Output Voltage: 1.5 Volts

ADC Value: 205


Output Voltage: 1.01 Volts

47
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 05
OBJECTIVE:
To interface an LCD (Liquid Crystal Display) screen with ATmega328P by sending required commands
and data

LAB OUTCOMES:
By the end of this lab, you would be able to:
1) Identify the pins and commands for controlling a 16x2 LCD
2) Interface a 16x2 LCD screen with ATmega328P and display different messages

“I don't care what it is, when it has an LCD screen, it makes it better.” — Kevin Rose

INTRODUCTION:
Display units like LEDs, 7-segment LED displays, LCD screens etc. play an important part in establishing
a good communication between the users and machines, and therefore, are vital for embedded systems.
Through display screens, the user gets a feeling of knowing the system’s working status. Consider the
examples of ATM machine, automatic washing machine or microwave ovens. They allow us to give input
through keypad or knobs or touch screens, and display useful messages on screens which guide us or show
the status of process. LCD screens are now seen everywhere due to their declining prices, ease of
programming due to an internal controller, and ability to display characters and graphics.
For learning purpose, we are interfacing our ATmega32P microcontroller with a standard 16x2 LCD screen.
16×2 LCD is named so because; it has 16 Columns and 2 Rows. Such dot-matrix LCDs are available in
different packages like 8x1, 8x2, 16x1, and 20x4.

16x2 LCD (Liquid Crystal Display)


Features
 The operating voltage of this LCD is 4.7V-5.3V
 It includes two rows where each row can produce 16-characters.
 The utilization of current is 1mA with no backlight
 Every character can be built with a 5×8 pixel box
 It can display alphabets, numbers and a few custom generated characters
 It can work on two modes (4-bit and 8-bit): In 4-bit mode we send the 4 bits (out of the total 8-
bits) at a time and in the 8-bit mode, we can send all 8-bits in one stroke.
 These are obtainable in Blue & Green Backlight
The LCD can display 32 characters in total and each character will be made of 5*8 (40) pixel dots. The
standard LCDs have HD44780 dot-matrix liquid crystal display controller / driver that is mounted on LCD
module itself. The function of this interface IC is to get the commands and data (sent over parallel data
lines) from the MCU and process them to display meaningful information onto our LCD Screen . Hence,
the MCU doesn’t directly have to deal with the 1280 pixels of LCD and their positions.
[Datasheet HD44780.]

48
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

LCD Pinout
Figure 1 shows the 16 pins of a 16x2 LCD and their names. Most of the LCDs have these 16 pins that are
used for connection according to their functionality. Let’s discuss the function of each pin one-by-one.

Figure 1: 16x2 LCD Pinout


Pin
# Description / Function Type Connection
Name
1 Connected to the ground of the MCU/
VSS Pin for LCD ground.
Source Power source.
2 Pin Connected to the supply pin of power
VDD Pin for LCD supply voltage.
source / +5V of MCU.
3 Contrast Control: Adjusts the Connected to a variable potentiometer
V0
contrast of the LCD. that can source 0-5V.
4 Connected to a digital output pin of
Register Select: Selects either MCU.
RS
command register or data register 0: Command Mode
1: Data Mode
5 Read/Write: Toggles the LCD Connected to a digital output pin of
between Read/Write operations. Control MCU.
RW
Read operation is rarely needed for Pins 0: Write Operation
information like cursor position etc. 1: Read Operation
6 The enable pin is used by the LCD to
latch information presented to its data
Enable: Must be held high to pins. When data is supplied to data pins,
E
perform Read/Write Operation a high-to-low pulse must be applied to
this pin in order for the LCD to latch in
the data present at the data pins.
7- In 4-bit mode, only 4 pins (D0-D3) are
14 Data/ connected to MCU digital output pins.
D0 to Data Bits (0-7): Pins used to send
Comman In 8-bit mode, all 8 pins (D0-D7) are
D7 command or data bits to the LCD.
d Pins connected to MCU digital output pins.
15 LED + Anode of backlight LED LED+ and LED- are connected to 5V
A
is given positive voltage. and Ground pins of MCU with a current
Backlight
16 LED - Cathode is connected to limiting resistor in series.
LED Pins
K ground to illuminate backlight
LED.

49
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

In this lab, we are interfacing the LCD with ATmega328P directly (parallel interface) and sending data
in 8-bit mode. Note that the 8-bit data interfacing is easier to program but uses 4 more pins.
LCD Commands
The following table hex code for the commands that are sent to LCD instruction register for the specified
functions.
Table 1: Hex code for Commands
Hex Code for
Function
Command to LCD
0E Display on, cursor on
01 Clear display screen
02 Return home
04 Decrement cursor (shift cursor to left
06 Increment cursor (shift cursor to right)
05 Shift display right
07 Shift display left
0F Display on, cursor blinking
80 Force cursor to beginning of first line
C0 Force cursor to beginning of 2nd line
38 Function Set: 2 lines and 5 × 8 matrix (D0–D7, 8-bit mode)
08 Display off, cursor off
18 Shift the entire display to the left
1C Shift the entire display to the right
Interfacing LCD with ATmega328P and C-Programming
The Figure 2 shows required connections for LCD 16 pins with ATmega328P in 8-bit data mode. The
R/W pin can be directly connected to ground instead of utilizing an I/O pin (as we are performing write
operation only).

Figure 2: LCD Connections with ATmega328P for 8-bit Mode

50
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

For controlling the LCD and sending commands and data, the following steps are needed. Remember, the
digital I/O pins connected with the LCD must be configured as output pins using DDRx registers as per
your connections.

Initializing or Configuring the LCD:

To initialize the LCD for 2-line and 8-bit operation, the following sequence of commands should be sent
to the LCD. Next we will show how to send a command to the LCD. After power-up you should wait
about 15ms before sending initializing commands to the LCD. If the LCD initializer function is not the
first function in your code you can omit this delay.

1) Function Set: 2 lines and 5 × 8 matrix (D0–D7, 8-bit mode) - 0x38


2) Display on, cursor blinking - 0x0E
3) Clear display screen - 0x01
After initialization, wait for 2msec.
Sending Command:

To send any of the commands from Table 1 to the LCD,


1) Make pin RS low for selecting command register. (R/W should be made low if not already grounded).
2) Put the command number on the data pins (D0–D7) i.e., use relevant PORTx register.
3) Send a high-to-low pulse to the E pin to enable the internal latch of the LCD.
Notice that after each command you should wait for some time (100us generally or for 2msec in some
cases like clear screen and return home) to let the LCD module run the command.
Sending Data:

To send data to the LCD


1) Make pins RS = 1 (for data register) and R/W = 0.
2) Put the data on the data pins (D0–D7) i.e., use relevant PORTx register.
3) Send a high-to-low pulse to the E pin to enable the internal latch of the LCD.
Notice that after sending data you should wait about 100 μs to let the LCD module write the data on the
screen.

Example Code and Subroutines


See the sample code for Example 1. It is written in in terms of LCD_DPRT, LCD_DDDR, LCD_DPIN.
These will be replaced by the PORT, DDR and PIN register of the port with which D0-D7 are connected.
In our example, it is port D. This is done to make the code more generalize, and to achieve this we have
used #define directive (#define causes the compiler to substitute token-string for each occurrence of
identifier in the source file). Similarly, LCD_CPRT, LCD_CDDR, LCD_CPIN are used to show the
relevant registers of port with which we have connected control pins (RS and E). Here, it is port B. For the
position of these pins is indicated by LCD_RS and LCD_E which is 0 and 1. If you change the hardware
connections, you have to update the relevant registers only once (i.e., at the start with the #define directive)
without changing the rest of the code or subroutines.
Based on the steps described earlier, 4 useful subroutines are defined before the main function.
51
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

1) lcdCommand: It takes hex code of command as input.

2) lcdData: It takes the data character to be displayed.

3) lcd_init: This performs initialization steps.

4) lcd_print: This takes a complete string to be printed and passes one character at a time by lcdData.

Figure 3: Connections for LCD Interfacing (8-bit Mode) with ATmega328P

Note that RW is connected with GND for writing.

52
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

#include <avr/io.h> void lcd_init()


#include <util/delay.h> {
#define LCD_DPRT PORTD //LCD DATA PORT LCD_DDDR = 0xFF;//making data port (output)
#define LCD_DDDR DDRD //LCD DATA DDR LCD_CDDR |= (1<<LCD_RS)|(1<<LCD_EN);
#define LCD_DPIN PIND //LCD DATA PIN //making control pins output pins
#define LCD_CPRT PORTB //PORT for LCD Control Pins LCD_CPRT &=~(1<<LCD_EN); //LCD_EN = 0
#define LCD_CDDR DDRB //DDR for LCD Control Pins _delay_ms(20); //wait for init.
#define LCD_CPIN PINB //PIN Reg for LCD Control Pins lcdCommand(0x38); //init. LCD 2-line, 8-bit mode
#define LCD_RS 0 //LCD RS (RS is connected at PB0) lcdCommand(0x0F); //display on blinking
#define LCD_EN 1 //LCD EN (EN is connected at PB1) lcdCommand(0x01); //clear LCD
_delay_us(2000); //wait
lcdCommand(0x06); //shift cursor right
void lcdCommand( unsigned char cmnd ) }
{
LCD_DPRT = cmnd; //send cmnd to data port void lcd_print( char * str )
LCD_CPRT &= ~ (1<<LCD_RS); //RS = 0 for command {
LCD_CPRT |= (1<<LCD_EN); //EN = 1 for H-to-L pulse unsigned char i = 0;
_delay_us(1); //wait to make enable wide while(str[i]!=0)
LCD_CPRT &= ~ (1<<LCD_EN); //EN = 0 for H-to-L pulse {
_delay_us(100); //wait to make enable wide lcdData(str[i]);
} i++ ;
// _delay_ms(100); //for typing effect
}

void lcdData( unsigned char data ) }


{
LCD_DPRT = data; //send data to data port int main(void)
LCD_CPRT |= (1<<LCD_RS); //RS = 1 for data {
LCD_CPRT |= (1<<LCD_EN); //EN = 1 for H-to-L pulse lcd_init(); //initialize
_delay_us(1); //wait to make enable wide _delay_ms(1000);
LCD_CPRT &= ~ (1<<LCD_EN); lcd_print("Congratulations!!");
//EN = 0 for H-to-L pulse lcdCommand(0xC0); //Cursor at the start of 2nd line
_delay_us(100); //wait to make enable wide lcd_print(" LCD works ");
} return 0;
}

Figure 4: Sample Code for Example 1 - LCD Interfacing (8-bit Mode) with ATmega328P
LAB TASKS
TASK 1: To test Example 1 using ATmega328P and 16x2 LCD
Program ATmega328P with the given code. Make connections to interface the LCD and test the results.
TASK 2: To test different commands for modifying LCD display
Modify the code and test different commands to make the display interesting.
 On line 1, display: <Your Name> (You can have more than 16 characters in the string).
 On line 2, display: <Roll # …… >
 Make the text moving (scrolling) continuously from left to right by using shift display commands.

53
Embedded Systems Lab Lab 05 LCD Interfacing
NED University of Engineering & Technology Electrical Engineering Department

Go-to Subroutine
Following is an interesting subroutine that allows you to move cursor at any specified location (y, x).
Where x is the line number (1 or 2) and y is character position (1 to 16).

void lcd_gotoxy(unsigned char x, unsigned char y)


{
char firstCharAdr[]={0x80,0xC0,0x94,0xD4}; //Table
lcdCommand(firstCharAdr[y-1] + x - 1);
_delay_us(100);
}

If you are interested, you can try this too!

54
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

LAB SESSION 06
OBJECTIVE:
To utilize SPI (Serial Peripheral Interface) protocol for interfacing the max6675 module with ATmega328P
and develop temperature measurement system based on the K-type thermocouple

LAB OUTCOMES:
By the end of this lab, you will be able to:
1) Recognize the difference between synchronous and asynchronous transmission
2) Identify the AVR ATmega328P pins associated with SPI communication
3) Identify the purpose of different fields of SPI registers
4) Program ATmega328P for SPI communication in master and slave modes for single-byte and
multiple-byte burst read/write
5) Interface SPI protocol-based module (Max6675) as slave with ATmega328P in master mode
6) Develop the complete system for temperature measurement and transmit result through USART to
PC

“Don’t expect what you don’t communicate clearly.” — Anonymous

INTRODUCTION:
In Lab 04, we discussed briefly about serial and parallel data transmission. USART was utilized for
asynchronous transmission and reception, which is one of the types of serial communication. In this lab,
we will explore Serial Peripheral Interface communication protocol which is synchronous. The SPI (serial
peripheral interface) is a bus interface connection incorporated into many devices. The SPI bus was
originally started by Motorola Corp. (now Freescale), but in recent years has become a widely used standard
adapted by many semiconductor chip companies as it’s faster, compact and results in reduced power
consumption. Let’s first understand the features of Serial Peripheral Protocol.
Serial Peripheral Interface Bus Protocol
SPI has a Master/Slave configuration. It has only one master device but can have multiple slaves. A master,
that initiates communication, is usually a microcontroller and the slaves can be a microcontroller, sensors,
ADC, DAC, LCD etc.
SPI is 4-wire protocol.
 SPI devices use only 2 pins for data transfer that are; SDI and SDO,
also called MISO (Master-In Slave-Out) and MOSI (Master-Out
Slave-In).
 The SPI bus has the SCLK or SCK (shift clock) pin to synchronize the
data transfer between two chips.
 The last pin is CE chip enable, also called SS Slave Select, which is
used to initiate and terminate the data transfer. It determines which
Figure 1: 4-wire SPI Bus
device the master is currently communicating with. Representation

55
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

Working of SPI
The system consists of two 8-bit wide shift registers, and a master clock generator. The SPI master initiates
the communication cycle when pulling low the slave select ̅̅̅ 𝑆𝑆. Master and slave prepare the data to be sent
in their respective shift registers, and the master generates the required clock pulses on the SCK line to
interchange data (one-bit at a time in each clock cycle). In SPI, the shift registers are 8 bits long. It means
that after 8 clock pulses, the contents of the two shift registers are interchanged. Data is always shifted from
master to slave on the MOSI, line, and from slave to master on the master MISO, line. After each data
packet, the master will synchronize the Slave by pulling high the slave select, ̅̅̅𝑆𝑆 line. It must be noted that
SPI is full duplex, meaning that it sends and receives data at the same time.

Figure 2: SPI Architecture and Master/Slave Interconnection


In connecting a device with an SPI bus to a microcontroller, we use the microcontroller as the master while
the SPI device acts as a slave.

Figure 3: SPI Interface of 1-master and 2-slave devices


Serial Peripheral Interface of AVR ATmega328P Microcontroller
The serial peripheral interface (SPI) of AVR allows high-speed synchronous data transfer between the
ATmega328P and peripheral devices or between several AVR devices. It can operate in master and slave
modes and allows LSB first or MSB first transfer options. Figure 3 shows the pins associated with
ATmega328 SPI and their connection pins on Arduino UNO board. Pin 11 or ICSP-4 is used as MOSI, Pin
12 or ICSP-1 is used as MISO, Pin 13 or ICSP-3 is connected with SCK and Pin 10 for SS.
56
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

Figure 4: Pin Configuration of ATmega328P for SPI Interface and Associated Arduino UNO
pinout

Figure 5: In-Circuit Serial Programming (ICSP) Header for SPI Communication


Table 1:
Pins Use Pin Configuration
Slave SS Used by Master device to enable and disable specific Input – For Slave
Select devices to communicate with. The SS pin is useful for
packet/byte synchronization to keep the slave bit Output – For Master
counter synchronous with the master clock generator. (usually)
When the SS pin is driven high, the SPI slave will
immediately reset the send and receive logic, and drop
any partially received data in the shift register.

Master-In MISO For sending data from Slave devices to Master device. Input – For Master
Slave-Out Output – For Slave
Master-Out MOSI For sending data from Master device to Slave devices. Input – For Slave
Slave-In Output – For Master
Serial SCK For clock pulses to synchronize data transmission from Input – For Slave
Clock Master devices. Output – For Master

57
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

SPI Data Modes and Clock Phase with Polarity


As we mentioned before in USART communication, transmitter and receiver must agree on a clock
frequency. In SPI communication, the master and slave(s) must agree on the CPOL (clock polarity) and
CPHA (clock phase), with respect to the data.
CPOL 0: The base value of the clock is zero. 1: The base value of the clock is one.
CPHA 0: Sample (Read) on the first clock edge. 1: Sample (Read) on the second clock edge.

Figure 6: Transfer Format as per SPI Clock Polarity and Phase


Based on this, 4 different modes of SPI are available.
Table 2: SPI Modes
CPOL CPHA Data Read and Change Time SPI Mode
0 0 Sample or read at rising edge. Setup or change data at falling edge. 0
0 1 Setup at rising edge. Sample at falling edge. 1
1 0 Sample at falling edge. Setup at rising edge. 2
1 1 Setup at falling edge. Sample at rising edge. 3

ATmega328P SPI Registers


In AVR three registers are associated with SPI. They are SPSR (SPI Status Register), SPCR (SPI Control
Register), and SPDR (SPI Data Register).
1) SPDR (SPI Data Register)

The SPI Data Register is a read/write register. To write into SPI shift register, data must be written to SPDR.
To read from the SPI shift register, you should read from SPDR. Writing to the SPDR register initiates data
transmission.

58
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

2) SPCR (SPI Control Register)

SPIE SPI Interrupt Enable


Setting this bit to one enables the SPI interrupt.
SPE SPI Enable
Setting this bit to one enables the SPI
DORD Data Order
The LSB is transmitted first if DORD is one; otherwise, the MSB is transmitted first.
MSTR Master/Slave Select
1: Selects master mode
0: Selects slave mode
CPOL Clock Polarity
0: The base value of the clock is zero.
1: The base value of the clock is one.
CPHA Clock Phase
0: Sample (Read) on the first clock edge.
1: Sample (Read) on the second clock edge.
SPR1, SPI Clock Rate Select 1 and 0
SPR0 SPI2X, SPR1, and SPR0 are combined to make different clock frequencies for master.

3) SPSR (SPI Status Register)

SPIF SPI Interrupt Flag


This bit is set when a serial transfer is completed (in master mode if SS is configured as an
output pin).
WCOL Write COLlision Flag
The WCOL bit is set if you write on SPDR during a data transfer
SPI2X Double SPI Speed
When the SPI is in master mode, setting this bit to one doubles the SPI speed.

Steps of Programming ATmega328P SPI


In accessing SPI devices, we have two modes of operation: single-byte and multiple-byte burst. To
program ATmega328P SPI, the following steps are needed depending upon master or slave modes. The

59
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

codes or subroutines are given here as sample for reference. In this lab, we will be operating our
microcontroller in the master mode only.

Single-Byte Reading and Writing in Master and Slave Mode


 ATmega328P SPI Master Initialization
To initialize ATmega328P as Master, do the following steps
1) Make MOSI, SCK, and SS pins directions as output.
2) Make MISO pin direction as input.
3) Make SS pin High.
4) Enable SPI in Master mode by setting SPE and MSTR bits in the SPCR register.
5) Set SPI Clock Rate Bits combination to define SCK frequency and clock polarity and phase.

#include <avr/io.h> //macros


#include <util/delay.h>
#define MOSI 3
#define MISO 4
#define SCK 5
#define SS 2
void spi_init_master( )
{
// configuring SPI pins
DDRB = (1<<MOSI) | (1<<SCK) | (1<<SS); //MOSI and SCK are output
DDRB &= ~ (1 << MISO_BIT); // input
// SPI Interrupt disabled
// SPI enabled
// Data order = MSB transmitted first
// Master mode enabled
// Clock polarity = Leading edge is rising
// Clock phase = Data is sampled on trailing edge
// SPI frequency = 16 Mhz / 16 = 1 Mhz
SPCR = (1 << 0); // SPI clock rate select 0 (SPR0)
SPCR |= (1 << 4); // Master/Slave select (MSTR)
SPCR |= (1 << 2); // Clock phase (CPHA)
SPCR |= (1 << 6); // SPI Enable (SPE) // SPI clock rate bit for SPI clock to be 1 Mhz
SPSR &= ~ (1 << 0); // Double SPI speed bit (SPI2X)
}

 SPI Master Write (Single-Byte)


Master writes data byte in SPDR. Writing to SPDR starts data transmission. 8-bit data starts shifting
out towards slave and after the complete byte is shifted, SPI clock generator stops, and SPIF bit
gets set. Follow the steps below:
1) Make SS low to select slave.
2) Load the 1 byte of data in the SPI shift register.

60
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

3) Wait till transmission is complete i.e., poll SPIF flag to become high.
4) Make SS high to deselect slave.

PORTB &= ~ (1 << SS);


void spi_write(char data) /* SPI write data function */
{
SPDR = data; /* Write data to SPI data register */
while (!(SPSR & (1<<SPIF))); /* Wait till transmission complete */
}
PORTB |= (1 << SS);

 SPI Master Read (Single-Byte)


1) Since writing to SPDR generates SCK for transmission, write dummy data in the SPDR register
even if you don’t want to send any data from master to slave.
2) Wait until the transmission is completed i.e. poll SPIF flag till it becomes high.
3) When the SPIF flag gets set, read requested received data in SPDR.
char spi_read( ) /* SPI read data function */
{
SPDR = 0xFF;
while (!(SPSR & (1<<SPIF))); /* Wait till reception complete */
return (SPDR); /* Return received data */
}

 ATmega328P SPI Slave Initialization and Read/Write Operation


1) In slave mode there is no need to set SCK frequency because the SCK is generated by the master,
but you must select the SPI mode (Clock Phase and Clock Polarity) and Data Order to match with
SPI mode and Data Order of the other side (master device).
2) Make MOSI, SCK, and SS pins direction of the device as input.
3) Make MISO pin direction of the device as output.
4) Enable SPI in slave mode by setting SPE bit and clearing MSTR bit.
The Slave SPI interface remains in sleep as long as the SS pin is held high by the master. It activates only
when the SS pin is driven low. Data is shifted out with incoming SCK clock from master during a
write operation. SPIF is set after the complete shifting of a byte.
For the read and write operations in slave mode, almost the same steps are followed as in master
mode.

void spi_init_slave( ) /* SPI Initialize function */


{
DDRB &= ~ ((1<<MOSI)|(1<<SCK)|(1<<SS)); /* Make MOSI, SCK, SS as input pins */
DDRB |= (1<<MISO); /* Make MISO pin as output pin */
SPCR = (1<<SPE); /* Enable SPI in slave mode */
}

61
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

char spi_receive( ) /* SPI Receive data function */


{
while(!(SPSR & (1<<SPIF))); /* Wait till reception complete */
return(SPDR); /* Return received data */
}
void spi_write(char data) /* SPI write data function */
{
SPDR = data; /* Write data to SPI data register */
while (!(SPSR & (1<<SPIF))); /* Wait till transmission complete */
}

Multiple-Byte Burst Reading and Writing in Master and Slave Modes


Burst mode reading or writing is an effective way to share multiple bytes from master / slave at once. To
do this,
1) Make SS low to select slave device.
2) Load the 1 byte of data in the SPI shift register.
3) Wait till transmission is complete.
4) Repeat step 2 and 3 till all bytes are transferred.
5) Make SS high to deselect slave.
We will practice this for interfacing our required module for this lab.

Max6675 Module with K-Type Thermocouple


The MAX6675 is a sophisticated thermocouple-to-digital converter with a built-in 12-bit analog-to-digital
converter (ADC). The MAX6675 also contains cold-junction compensation sensing and correction, a digital
controller, an SPI-compatible interface, and associated control logic.
Features
 Direct Digital Conversion of Type -K Thermocouple Output
 Cold-Junction Compensation: Simple SPI-Compatible Serial Interface
 12-Bit, 0.25°C Resolution
 Open Thermocouple Detection
Datasheet: Max6675
The function of the thermocouple is to sense a difference in temperature between two ends of the
thermocouple wires. The thermocouple’s hot junction can be read from 0°C to +1023.75°C. The cold end
(ambient temperature of the board on which the MAX6675 is mounted) can only range from -20°C to
+85°C. While the temperature at the cold end fluctuates, the MAX6675 continues to accurately sense the
temperature difference at the opposite end. The MAX6675 senses and corrects for the changes in the
ambient temperature with cold-junction compensation. The device converts the ambient temperature
reading into a voltage using a temperature-sensing diode. To make the actual thermocouple temperature
measurement, the MAX6675 measures the voltage from the thermocouple’s output and from the sensing
diode. The device’s internal circuitry passes the diode’s voltage (sensing ambient temperature) and
thermocouple voltage (sensing remote temperature minus ambient temperature) to the conversion function
stored in the ADC to calculate the thermocouple’s hot-junction temperature. The ADC adds the cold-
62
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

junction diode measurement with the amplified thermocouple voltage and reads out the 12-bit result onto
the SO pin. A sequence of all zeros means the thermocouple reading is 0°C. A sequence of all ones means
the thermocouple reading is +1023.75°C.

Figure 7: Typical connections of max6675 with microcontroller


As per the datasheet, following sequence of operation generates results from the max6675 module.
Force CS low to output the first bit on the SO pin. A complete serial interface read requires 16 clock cycles.
Read the 16 output bits on the falling edge of the clock. The first bit, D15, is a dummy sign bit and is always
zero. Bits D14–D3 contain the converted temperature in the order of MSB to LSB. Bit D2 is normally low
and goes high when the thermocouple input is open. D1 is low to provide a device ID for the MAX6675
and bit D0 is three-state.

Figure 8: Serial Interface Protocol for Max6675

Figure 9: SO Output from Max6675

63
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

Example 1: Programming ATmega328P SPI in master mode and measure


temperature through Max6675
Based on the information regarding max6675 module, we need to interface it with ATmega328P through
SPI protocol and 2-byte burst read is required. Moreover, the 12 date bits from 16-bit output need to be
extracted. The final result is then multiplied with resolution to get exact temperature. This is done in a
sample code below.
#include <avr/io.h> data = data << 8;
#include <util/delay.h> //write dummy value again in the SPI data
#define MOSI 3 register to read last 8 bits from slave
#define MISO 4 SPDR = 0xFF;
#define SCK 5 while ( !(SPSR & (1 << 7)) ){} // wait till SPI
#define SS 2 interrupt flag (SPIF) gets high
// writing SPI data to lower 8 bits of the data
void spi_init() variable
{ data |= SPDR;
// configuring SPI pins // disable chip
DDRB = (1<<MOSI)|(1<<SCK)|(1<<SS); //MOSI spi_deselect();
and SCK are output return data;
DDRB &= ~(1 << MISO_BIT); // input }
SPCR = (1 << 0); // SPI clock rate select 0 (SPR0)
SPCR |= (1 << 4); // Master/Slave select (MSTR) float read_Thermocouple()
SPCR |= (1 << 2); // Clock phase (CPHA) {
SPCR |= (1 << 6); // SPI Enable (SPE) uint16_t data;
// SPI clock rate bit for SPI clock to be 1 Mhz
SPSR &= ~(1 << 0); // Double SPI speed bit // read SPI data
} data = spi_read16();
_delay_ms(1);
void spi_select()
{ // Bit 2 gets high if thermocouple input is
PORTB &= ~(1 << SS); open
} if (data & 0x4)
return -1;
void spi_deselect()
{ // discarding 3 LSB bits
PORTB |= (1 << SS); data >>= 3;
} // factor taken from datasheet of MAX6675
return data*0.25;
uint16_t spi_read16()
{ }
uint16_t data;
// select chip to enable data transfer int main()
spi_select(); {
_delay_ms(1); float temp;
// write dummy value in the SPI data register to spi_init();
read first 8 bits from slave
SPDR = 0xFF; while(1)
while ( !(SPSR & (1 << 7)) ){}; // wait till SPI {
interrupt flag (SPIF) gets high temp = read_Thermocouple();
// read data register _delay_ms(500);
data = SPDR; }
}
// left shifting data to 8 bits

64
Embedded Systems Lab Lab 06 SPI Protocol and Max6675 Interfacing
NED University of Engineering & Technology Electrical Engineering Department

Task 1: To develop a temperature measurement system by interfacing Max6675


to microcontroller
1) Modify the code in Example 1 to transmit the temperature through USART to PC. Utilize the Lab
04 codes.
2) Build the code and program ATmega328P with it.
3) Complete the interfacing of max6675 module with microcontroller by making all the required
connections.
4) Test your developed system. The serial terminal of PC should display room temperature value.
5) Test it in different temperature conditions and observe output.
Congratulations! You have successfully developed a small system that measures and displays accurate
temperature from 0 to 1023°C.

Interesting Activity: *(Optional)


You can connect Oscilloscope probes to view SCK, MISO and SS signals at different channels
simultaneously to verify the synchronous communication. Observe that SCK signal is only available when
SS is set low and clock is generated. With respect to the clock signals, observe the data bits (D15 to D0).
Read the 16-bit output from Max6675 and try to verify the temperature obtained using the extracted 12-bit
number (D14-D3).

65

You might also like