You are on page 1of 38

LESSON 9: UART

An embedded system often requires a means for communicating with the external world
for a number of possible reasons. It could be to transferring data to another device,
sending and receiving commands, or simply for debugging purposes. One of the most
common interfaces used in embedded systems is the universal asynchronous
receiver/transmitter (UART). When a board arrives in the hands of the software/firmware
team, the first step is typically to get the debug console functional. The debug console is a
serial interface which historically is implemented as RS-232 to connect with a PC serial port.
These days most PCs not longer have a serial port, so it is more commonly seen
implemented using USB, however the concept is the same. In this lesson, we will learn a
bit about the theory behind UART and RS-232, learn how to write a simple UART driver for
the MSP430, and create a menu which gives the user the ability to change the frequency
of the blinking LED during runtime.
It is important to distinguish the difference between the terms UART and RS-232. The UART
is the peripheral on the microcontroller which can send and receive serial data
asynchronously, while RS-232 is a signalling standard. RS-232 has no dependency on any
higher level protocol, however it does have a simple layer 1 (physical layer) set of standards
which must be followed. The UART module may support several features which allow it to
interface with various signaling standard such as RS-232 or RS-485 – another serial
interface commonly used in industrial applications.

RS-232

RS-232 is a point-to-point signalling standard, meaning only two devices can be
connected to each other. The minimum connection required for bidirectional
communication is three signals: transmit (TX), receive (RX), and ground. The separate
RX and TX lines mean that data can flow in both directions at the same time. This is called
full-duplex and it is the standard means for communicating over serial. However,
depending on the higher level protocols, there may be a need to block the transmitter while
receiving. This is called half-duplex. Hardware flow control can also be enabled in order
to mitigate the flow of data. Two optional lines RTS and CTS are provided for this function.
Typically RS-232 is used without hardware flow control and at full duplex. We are not
going to go into details on all the possible configurations, however you can read about it
here if you are interested.

RS-232 signals are different than than what we are used to in the digital world because
the voltage switches between negative and positive values. The standard defines signals
which typically vary from -5V to +5V, but can as much as -15V to +15V. The idle state of
the line is at the negative voltage level and is referred to as a ‘mark’. The logical value of
a mark is one (1). The positive voltage is called a ‘space’, and indicates a logic zero (0).
To begin a transmission of data, a start bit (space) is sent to the receiver. Then the data
is transmitted. The data can be in several possible formats depending what is supported

by both devices. To end a transmission, a stop bit (mark) is sent to the receiver, and the
held in the idle state. At least one stop bit is required, but two stop bits are often supported
as well.

When hooking up RS-232 to an MCU it is important to remember that the voltage levels
supported by the IO are different (0V – 3.3V), so an external transceiver is required to
convert the signals to the appropriate levels. If you try to connect RS-232 directly to the
MSP430 or most other microcontrollers it will not work and likely cause some damage.
The MAX232 and variants are some of of the most common RS-232 transceivers on the
market. It is extremely simple to use and can be easily breadboarded. Here is an
example of one I have built:

Fortunately, the MSP430 Launchpad has a serial to USB converter built right onto the
the board so this additional equipment is not required. Therefore, we won’t cover how to
build it in this tutorial, but if you would like to know more feel free to shoot me an email.
We will look in more detail at the MSP430 implementation later on.

Universal asynchronous receiver/transmitter (UART)
UART peripherals typically have several configurable parameters required to support
different standards. There are five parameters which must be configured correctly to
establish a basic serial connection:

▪ Baud rate: Baud rate is the number of symbols or modulations per second. Basically,
the baud rate indicates how many times the lines can change state (high or low)
per second. Since each symbol represents one bit, the bit rate equals the baud
rate. For example, if the baud rate is 9600, there are 9600 symbols sent per
second and therefore the bit rate is 9600 bits per second (bps) .
▪ Number of data bits: The number of data bits transmitted is typically between 5 and 8,
with 7 and 8 being the most common since an ASCII character is 7 bits for the
standard set and 8 bits for the extended.
▪ Parity: The parity can be even, odd, mark or space. The UART peripheral calculates
the number of 1s present in the transmission. If the parity is configured to even
and the number of 1’s is even then the parity bit is set zero. If the number of 1s is
odd, the parity bit is set to a 1 to make the count even. If the parity is configured to
odd, and the number of 1s is odd, then parity bit is set to 0. Otherwise it is set to 1
to make the count odd. Mark and space parity mean that the parity bit will either
be one or zero respectively for every transmission.
▪ Stop bits: The number of stop bits is most commonly configurable to either one or two.
On some devices, half bits are supported as well, for example 1.5 stop bits. The
number of stop bits determines how much of a break is required between
concurrent transmissions.
▪ Endianess: Some UART peripherals offer the option to send the data in either LSB
(least significant bit) or MSB (most significant bit). Serial communication of ASCII
characters is almost always LSB.
All of these parameters must be set to the same configuration on both devices for
successful communication. The following image is an example of a UART transmission.

Image courtesy of one of our very active members, Yury. Thanks!
Here we have a 5 bit transmission with an odd parity. Since there are an odd number of
1s in the transmission, the parity bit is 0. The data bit closest to the start bit is the LSB.
The number of stop bits is not defined since we only see one transmission. However if
there was 1 stop bit and we were running at 9600 baud, this configuration would be
abbreviated 9600 5O1. Other common configuration include:
9600 7E1 – 9600 baud, 7 bits data, even parity and 1 stop bit
9600 8N1 – 9600 baud , 8 bits data, no parity and 1 stop bit
115200 8N1 – 115200 baud, 8 bits data, no parity and 1 stop bit
The MSP430 UART
The MSP430 provides a module called the USCI (universal serial communications
interface) which supports multiple types of serial interfaces. There are two variants of the
USCI module each of which support specific interfaces:
USCI_A: UART and SPI
USCI_B: SPI and I2C

The register map for the USCI_A module is as follows: TI MSP430x2xx Family Reference Manual (SLAU144J) The first register. one or more of each of these modules. there are many registers and settings.A given device may have none. TI MSP430x2xx Family Reference Manual (SLAU144J) ▪ UCPEN: Parity enable ▪ 0 Parity disabled ▪ 1 Parity enabled ▪ UCPAR: Parity mode selection ▪ 0 Odd parity ▪ 1 Even parity ▪ UCMSB: MSB (most significant bit) first selection ▪ 0 LSB first ▪ 1 MSB first ▪ UC7BIT: Data length ▪ 0 8-bit data ▪ 1 7-bit data ▪ UCSPB: Number of stop bits ▪ 0 One stop bit ▪ 1 Two stop bits ▪ UCMODEx: USCI mode asynchronous mode (only valid when UCSYNC=0) ▪ 00 UART mode ▪ 01 Idle-line multiprocessor mode ▪ 10 Address-bit multiprocessor mode . Since USCI_A actually supports multiple standards. depending on its implementation. It is important to check in the datasheet to see exactly what is supported in the device being used. We will only concentrate on those relative to this lesson. UCAxCTL0 or USCI_Ax Control Register 0 contains the configuration for the protocol.

Sections 15.3. interrupts etc. .12 of the family reference manual discuss how to calculate these values based on the desired baud rate.13 with suggested values for commonly used baud rates and clock selections. TI MSP430x2xx Family Reference Manual (SLAU144J) ▪ UCSSELx: USCI clock source selct ▪ 00 UCLK external clock source ▪ 01 ACLK ▪ 10 SMCLK ▪ 11 SMCLK ▪ UCRXEIE: Erroneous character received interrupt enable ▪ 0 Characters received with errors are dropped and no interrupt raised ▪ 1 Characters received with errors are retained and UCAxRXIFG is set ▪ UCBRKIE: Break character received interrupt enable ▪ 0 Receving a break character does raise an interrupt ▪ 1 Receiving a break character raises UCAxRXIFG ▪ UCDORM: Set USCI module to sleep mode (dormant) ▪ 0 Not in sleep mode ▪ 1 Sleep mode – certain characters can still raise an interrupt on UCAxRXIFG ▪ UCTXADDR: Transmit address marker – only valid for address-bit multiprocessor mode ▪ 0 Next frame is data ▪ 1 Next frame is marked as an address ▪ UCTXBRK: Transmit break – all symbols in the transmission are low ▪ 0 Next frame is not a break ▪ 1 Next frame transmitted is a break ▪ UCSWRST: Module software reset – USCI is held in reset by default on power on or device reset and must be cleared by software to enable the module ▪ 0 USCI operational – not in reset ▪ 1 Reset USCI module Next we have the two baud rate control registers UCAxBR0 and UCAxBR1 as well as the modulation control register UCAxMCTL.9 – 15. configures the USCI module in terms of clocking. UCAxCTL1.3. The UCAxSTAT register contains the status of the module. However.3. To save us (and the MSP430) some math. we will be using this table as a reference. enable.▪ 11 UART mode with automatic baud rate detection ▪ UCSYNC: Synchronous/Asynchronous mode ▪ 0 Asynchronous (UART) ▪ 1 Synchronous (SPI) The second control register. USCI_Ax Control Register 1. TI has also provided us with a nice table in section 15.

Must not be cleared by software ▪ 0 No overrun error detected ▪ 1 Overrun error detected ▪ UCPE: Parity error detect ▪ 0 No parity error detected ▪ 1 Parity error detected ▪ UCBRK: Break frame detect ▪ 0 No break frame detected ▪ 1 Break frame detected ▪ UCRXERR: Character received with an error. This bit is cleared by reading UCAxRXBUF ▪ 0 Character received does not contain an error ▪ 1 Character received contains error ▪ UCADDR: Address received – only in address-bit multiprocessor mode ▪ 0 Data received ▪ 1 Address received (address bit set) ▪ UCIDLE: Idle line detected – only in idle-line multiprocessor mode ▪ 0 Idle line not detected ▪ 1 Idle line detected ▪ UCBUSY: USCI module busy – either transmit or receive operation in progress ▪ 0 USCI not busy ▪ 1 USCI operation in progress The SFR (special function register) IE2 contains the interrupt enable bits for the USCI module.TI MSP430x2xx Family Reference Manual (SLAU144J) ▪ UCLISTEN: Loopback (listen) enable. . ▪ UCA0TXIE: USCI_A0 transmit interrupt enable ▪ 0 Transmit interrupt disabled ▪ 1 Transmit interrupt enabled ▪ UCA0RXIE: USCI_A0 receive interrupt enable ▪ 0 Receive interrupt disabled ▪ 1 Receive interrupt enabled The SFR IFG2 contains the interrupt enable bits for the USCI module. TI MSP430x2xx Family Reference Manual (SLAU144J) Note. the undefined bits may be used by other modules depending on the specific device.e. When enabled TX is fed into the RX ▪ 0 Loopback disabled ▪ 1 Loopback enabled ▪ UCFE: Framing error detect ▪ 0 No framing error detected ▪ 1 A frame with a low stop bit detected ▪ UCOE: Overrun error – a character was received and stored in UCAxRXBUF before it was read by software (i. One or more other error bits will be set when this bit is set. character is dropped). See the device data-sheet for more information.

when data is received on line. file descriptors and all the rest that comes along with the actual implementation. there are plenty of ways using the standard library to print and read from the console. When the USCI is configured for 7-bit mode. however there are others such as puts. When UCAxRXBUF is read by software. the undefined bits may be used by other modules depending on the specific device. unless you are running a full blown OS such as Linux. the data is copied to UCAxTXBUF. it is stored in UCAxRXBUF and UCAxRXIFG (receive interrupt flag) is set. there are no actual file descriptors and often it accesses the UART directly. Try to use snprintf or printf and you will soon run of of space in the text section (where the code goes). UCAxABCTL are not required for standard UART mode and therefore will not be covered in this lesson. The code For this tutorial we want to implement a UART driver with some simple APIs which can be used to print a menu and accept user input. The data is held in this register until it is read by software or another frame is received. UCAxTXIFG will be set. the MSB of both of these registers is unused. When programming for your desktop. To receive and transmit data respectively there are two 8-bit registers. putchar. the standard C library we have as part of gcc (newlib). The goal is to create a menu which will allow us to change the frequency of the blinking LED. ▪ UCA1TXIFG: USCI_A0 transmit complete interrupt flag ▪ 0 No interrupt pending ▪ 1 Interrupt pending ▪ UCA1RXIFG: USCI_A0 receive interrupt flag ▪ 0 No interrupt pending ▪ 1 Interrupt pending Note that these values fields are only for USCI_A0. This also clears UCAxTXIFG (transmit complete interrupt flag). UCAxRXIFG is cleared. however we do not have the concept of stdin and stdout. Perhaps it would fit on some of the bigger devices. To initiate a transfer. We will not spend much time on the implementation of the menu as it is not important for the purposes of learning how to use the UART. printf may not support all the formatters. UCAxRXBUF and UCAxTXBUF. Once the transmission is complete. UCAxIRRCTL. The former 2 are for infrared devices. in which case it is overwritten and UCAxSTAT[UCOE] is set. In fact.TI MSP430x2xx Family Reference Manual (SLAU144J) Note. . however in embedded programming. while the latter is for UART with auto baud rate detection. Get the latest code from github to get started. has the full implementation. and getchar which are more limited but simpler to implement. Similarly. Our UART driver will follow this model. If there is a second USCI_A module (USCI_A1). the standard C library is often specifically written only with the functionality required. Registers UCAxIRTCTL. The most commonly used is printf. For example. See the device data-sheet for more information. however it is too big (takes too much memory) for the MSP430G2553. equivalent fields are in registers UC1IE and UC1IFG respectively.

which is for the most part a placeholder structure for any attributes which need to be configured.h located in the src and include directories respectively. 25 } 26 } 27 28 return status. The MSP430G2553 has one USCI_A module.Before implementing the functions to read and write.UCAxBR1. 20 UCA0MCTL = baud_tbl[i]. Two new files have been created. the baud rate is the only member. The baud rate must be defined as a 32-bit unsigned integer because as we learned earlier.h. i++) { 12 if (baud_tbl[i]. The function uart_init is implemented as follows: 1 int uart_init(uart_config_t *config) 2 { int status = -1.UCAxMCTL. 19 UCA0BR1 = baud_tbl[i]. 29 } 30 31 32 The function takes one argument of type uart_config_t from include/uart. 24 status = 0. so we will write a the driver specifically for it.only configure once */ 5 if (UCA0CTL1 & UCSWRST) { 6 size_t i. and this integer value does not fit into the native integer size of 16 bits. . } 14 } 15 16 if (i < ARRAY_SIZE(baud_tbl)) { 17 /* Set the baud rate */ 18 UCA0BR0 = baud_tbl[i]. 7 8 /* Set clock source to SMCLK */ UCA0CTL1 |= UCSSEL_2. The UART configuration we will be using is 9600 8N1. 21 22 /* Enable the USCI peripheral (take it out of reset) * 23 UCA0CTL1 &= ~UCSWRST.UCAxBR0.c and uart. 4 } uart_config_t. we must initialize the USCI peripheral. i < ARRAY_SIZE(baud_tbl). 1 typedef struct 2 { 3 uint32_t baud. 3 4 /* USCI should be in reset before configuring . uart. baud rates up to 115200 are common. For now.baud == config->baud) { 13 break. 9 10 /* Find the settings from the baud rate table */ 11 for (i = 0.

Therefore the table will have only one entry as defined below: 1 const struct baud_value baud_tbl[] = { 2 {9600. 4 5 if (IFG2 & UCA0RXIFG) { 6 chr = UCA0RXBUF. 4 uint16_t UCAxBR0. the interrupt flag IFG2[UCA0RXIFG] can be read to determine if a character has been received. To set the baud rate. it is more efficient to simply save the register values in a table that can be referenced for a given baud rate.The USCI module is held in reset by default. the character is read from UCA0RXBUF. 6 uint16_t UCAxMCTL. A note on the above code: the ‘for’ loop iterates through the baud rate table using a macro ARRAY_SIZE which is defined in a new file include/defines. since this is the maximum of the serial USB interface of the Launchpad. we will use the table from the reference manual. it returns -1. Rather than calculating the values for each register. commonly referred to in *nix talk as EOF (end of file). no parity and 1 stop bit. you can use the sizeof() operator to find number of bytes required to store the whole array. The default register values for USCA0CTL0 configure the device for 8 bit data. Currently we will only support 9600 baud. The register values are copied from the table into the peripheral. which reads one character at a time from the UART. 104. which is fairly complex and would be quite heavy mathematically for the MSP430. 1 int uart_getchar(void) 2 { 3 int chr = -1. . This file will be the default location to put any generic macros or hash defines. However. so no further configuration is required. Next the USCI clock is set to SMCLK. It is important to keep the USCI in reset until the configuration is complete and ready to communicate. The initialization function will take the baud rate passed in the configuration structure and iterate through the list of supported baud rates until a match is found. This particular macro makes it very simple to calculate the size of an array. 0. If it has. We can easily check if it has already been initialized by checking the value of UCA0CTL1[UCA0CTL1]. which is 1MHz. In this simple implementation. The table structure looks like this: 1 struct baud_value 2 { 3 uint32_t baud. Since in C an array must have a defined size at compile time. 0x2} 3 }. Dividing this value by the size of one element in the array – by convention we use the first one – gives the number of elements in the array. we will not implement any UART interrupts since polling is not required. The module is taken out of reset and is ready to go. 7 }. 5 uint16_t UCAxBR1. The first IO function we have is uart_getchar. If there are no characters to read.h. This value will be determined at compile time so there is no runtime penalty for the division.

which indicates the end of the string. This is efficient in the sense that while the UART is pushing out the data. It is cleared automatically by the hardware when the data is put into the transmit buffer UCA0TXBUF. 11 12 /* Transmit data */ 13 UCA0TXBUF = *str. 1 int uart_putchar(int c) 2 { 3 /* Wait for the transmit buffer to be ready */ 4 while (!(IFG2 & UCA0TXIFG)). which is really just an extension of uart_putc that can print a string rather than individual characters. When the interrupt flag is set. 10 } The next function to implement is uart_putchar. 4 5 if (str != NULL) { 6 status = 0. 7 8 while (*str != '\0') { 9 /* Wait for the transmit buffer to be ready */ 10 while (!(IFG2 & UCA0TXIFG)).The implementation is exactly the same as uart_putc. to print a character to the console. except we iterate through the string until NULL is found. but we’ll cover that in a later lesson. There is even more efficient possibilities using interrupts. add a carriage return */ 16 if (*str == '\n') { 17 /* Wait for the transmit buffer to be ready */ 18 while (!(IFG2 & UCA0TXIFG)). 1 int uart_puts(const char *str) 2 { 3 int status = -1. 8 9 return 0.7 } 8 9 return chr. that this function can return before the transmission has completed. the USCI module is ready for more data. 5 6 /* Transmit data */ 7 UCA0TXBUF = (char ) c. 10 } Note. 20 } . The final function is uart_puts. 19 UCA0TXBUF = '\r'. the CPU has some time to get the next piece of data ready or perform some other task. 14 15 /* If there is a line-feed. Before transmitting we have to check that the transmit buffer is ready – it has completed the previous transmission – by reading the transmit interrupt flag IFG2[UCA0TXIFG].

but because we do not want to conflict with the standard C library routines. 1 /* Set P1. the pin muxing of P1.c file. When writing to the terminal in Linux. For example. Below is an excerpt from the board_init function. First we need to add the initialization to the board. This is called a carriage return. 10 11 /* Global interrupt enable */ 12 __enable_interrupt(). This means however that none of the standard header files are accessible. 13 14 watchdog_enable().2 must be configured to USCI TX and RX. Depending on how the library is implemented. In a terminal emulator however.1 and P1. we prefixed all these functions with uart_ not only because they are part of the UART API. they must both be received (this can be sometimes be disabled).1 and P1. “HellonWorldn” would display like this: To avoid having to use “\n\r” everywhere. If you really want to write a custom standard C library. 3 4 /* Enable interrupt on P1. but it can be unsafe and unpredictable. and therefore must all be redefined in your software. by checking if the current character is a line feed and automatically adding a carriage return. such as Tera Term or minicom. there are linker options which can tell gcc to not include them.3 */ 5 P1IE |= 0x08. whose ASCII character representation is ‘\r’. otherwise the text will continue from the same position on the next line. 9 P1SEL2 |= 0x6. 23 } 24 } 25 26 return status. The UART driver must now be integrated with our existing application. we can make this function handle both. the head also has to return back to the start (left side) of the page. which when you press enter. the roller would move the paper up one line. using ‘\n’ to create a new line is valid. These two characters together make what is today commonly called a newline.21 22 str++. It is important to note. . However. The terminology derives from the good old days of typewriters. which we do all the time by pressing the enter key.2 for UART (USCI_A0) */ 8 P1SEL |= 0x6. However. 6 7 /* Configure P1. it depends on the terminal settings and may not always be the case.3 interrupt to active-low edge */ 2 P1IES |= 0x08. The character ‘\n’ is line feed character. In addition. 27 } There is one additional feature that I like to add for robustness. you may be able to override some of the functions.

21 } Next we can start modifying the main loop to create our menu. which initializes the menu. This function will also display the menu. 5 }. The function does not block.h provides a structure called menu_item which contains the text and the callback of the each selection. and if the enter key is pressed. Otherwise. Then the array is passed into the function menu_init in src/menu. The caller creates a list of menu items representing with the desired options and callbacks. size_t count) 2 { 3 /* Limit menu size to 9 options */ 4 if (count < 9) { 5 count = 9. This is required for our application because we don’t want the menu to block all other functionality. the function calls uart_getchar to read the characters received from the UART. meaning that if there is no user input. 12 } To read the user input and make a selection.c. 1 void menu_init(const struct menu_item *menu. but if you have any questions about it feel free to ask. Whenever a character is received. so that the user can see what was typed. The implementation of the menu isn’t all that important so we won’t go into much detail. 1 struct menu_item 2 { 3 const char *text. 4 int c = uart_getchar(). it must be echoed back to the console. Internally. it will return immediately. it will determine if the value entered is within the limits of the menu and will execute the callback. It accepts numbers only. To build a menu. 9 _current_menu_size = count. 6 } 7 8 _current_menu = menu. it will feel like they are typing into the abyss. 1 void menu_run(void) 2 { 3 static unsigned int value = 0. 10 11 display_menu().baud = 9600. as typically we do not want it to be modified. 4 int (*handler)(void). the API defined in include/menu.15 16 /* Initialize UART to 9600 baud */ 17 config. 18 19 if (uart_init(&config) != 0) { 20 while (1). 5 . It is best to create this array as a static const. The important thing is to understand how the UART is being accessed. menu_run can be invoked.

1].handler != NULL) { 14 uart_puts("\n").'0'. 16 } else if ((c == '\n') || (c == '\r')) { 17 uart_puts("\n"). 15 uart_putchar(c).6 if ((c >= '0') && (c <= '9')) { 7 value *= 10. 15 if (_current_menu[value . Unlike menu_run. Often a menu option itself will require user input. 14 value += c . 9 10 watchdog_pet(). this functions is blocking but takes care of petting the watchdog. 25 } else { 26 /* Not a valid character */ 27 } 28 } One more API is provided more as a helper function for the callback functions. 11 12 if ((c >= '0') && (c <= '9')) { 13 value *= 10.'0'. 9 uart_putchar(c). 1 unsigned int menu_read_uint(const char *prompt) 2 { 3 unsigned int value = 0. 21 } 22 23 display_menu(). menu_read_uint.handler() != 0) { 16 uart_puts("\nError\n").1]. 18 break. 19 } else { . 4 5 uart_puts(prompt). 10 } else if ((c == '\n') || (c == '\r')) { 11 if ((value > 0) && (value <= _current_menu_size)) { 12 /* Invoke the callback */ 13 if (_current_menu[value . and in our case we want to be able to input a frequency for the blinking LED. 24 value = 0. 17 } 18 } 19 } else { 20 uart_puts("\nInvalid selection\n"). 6 7 while (1) { 8 int c = uart_getchar(). 8 value += c . It will return the unsigned integer value enter by the user.

the user has to stop and restart the the timer using the push button. set_blink_freq}. therefore to change the blinking frequency. Even though this variable is global. When the user selects option 1. It is only modified by the user in the callback. Then the frequency entered is divided by 1000 to get the timer timeout period in ms. it is important to see how the variable is being used to set the timer period. 7 } 8 9 return (value > 0) ? 0 : -1. change the frequency of the blinking LED. the access is sequential and does not require a critical section or a volatile identifier either. First we build the menu in the global namespace with a single option. will be invoked. 4 }. The timer API only permits the period to be set when it is created. we make a call to menu_run in order to continuously monitor for user input. set_blink_freq. 1 int main(int argc. 1 static int set_blink_freq(void) 2 { 3 const unsigned int value = menu_read_uint("Enter the blinking 4 5 if (value > 0) { 6 _timer_ms = 1000 / value. and it will be printed out the terminal. we can take a look at main. 10 } The value returned from menu_read_uint is validated to make sure there is no dividing by zero. we do not have to disable interrupts as we have done with the timers in the last lesson. 5 6 if (board_init() == 0) { 7 int timer_handle = -1. Note that we use the macro ARRAY_SIZE here as well to pass in the number of menu items. 4 (void) argv. The value is stored in a new global variable called _timer_ms.c. 8 . Next the menu is initialized with our main menu. and read by the main while loop. In addition. 1 static const struct menu_item main_menu[] = 2 { 3 {"Set blinking frequency". Therefore.20 /* Not a valid character */ 21 } 22 } 23 24 return value. 25 } To put it all together. the callback function defined in the main menu. char *argv[]) 2 { 3 (void) argc. In the existing while loop. Then in our main() function we print out a welcome message using the uart_write() function.

20 menu_run(). If your board is older than rev 1.9"). I suspect it will be the same. 17 18 while (1) { 19 watchdog_pet(). 14 uart_puts("\n********************************************* 15 16 menu_init(main_menu. blin 29 } 30 } else { 31 if (timer_handle != -1) { 32 timer_delete(timer_handle). They made some changes between rev 1.4 boards. On the Launchpad. but if not please inform me. 21 22 /** 23 * If blinking is enabled and the timer handle is 24 * negative (invalid) create a periodic timer 25 */ 26 if (_blink_enable != 0 ) { 27 if (timer_handle < 0) { 28 timer_handle = timer_create(_timer_ms. In both cases. so the jumper settings between the two are different. since you need to cross the pins like this: . 40 } Note how the timer_create function now takes the variable _timer_ms rather than the hardcoded value 500 as it did previously. 33 timer_handle = -1. the board is shipped with the jumpers set for software UART. it is sometimes implemented bit-banged using standard GPIOs rather than with the USCI peripheral as we have. The setup Since UART is relatively slow.5 to facility this functionality. On the rev 1. 1. TI has given us the option to use either software UART (bit-banging) or the hardware UART (USCI) with some jumper settings on the board.org"). ARRAY_SIZE(main_menu)). 12 uart_puts("\nVersion: 0. therefore we have to change them. 34 } 35 } 36 } 37 } 38 39 return 0.4 and 1.9 uart_puts("\n********************************************* 10 uart_puts("\nSimply Embedded tutorials for MSP430 Launchpa 11 uart_puts("\nsimplyembedded. you will need some jumper cables.4. 13 uart_puts("\n"__DATE__).

On rev 1. they made it a bit easier and you simply need to rotate the two jumpers 90 degrees as follows: .5.

especially in Linux. This is normal. if you check in the device manager. and TI supplies drivers for both channels (more on this later).Now your hardware should be ready to go. you will see that the device is not found. When you connect your Launchpad to the USB port on your computer. What they tried to do is valid. and CDC (communications device class) for the UART. the device will enumerate as two classes: HID (human interface device) required for the programming and debugging. In Windows. connect the debugger using mspdebug. However. On Linux (running as a host). The way the debugger and serial port were implemented on the Launchpad is somewhat flawed. Only one can run at a time. but for some reason it is unfortunately quite flakey. but what’s worse the CDC channel doesn’t . and now you lost your serial connection. which is a bit inconvenient. the CDC channel comes up as /dev/ttyACMx (where x is an integer value) and can be read directly as if it were a regular serial port.

as usual. Now heres the trick to this method. but its under export control so unfortunately that wasn’t an option. Now in Linux. 2 Now open the device manager in Windows. I was going to make the drivers easily accessible. It should create a new directory under your C drive called ‘ti’. I can answer any questions you may have. You should see the serial output being printed at this time. When you attach the Launchpad to VirtualBox. a Windows host and Linux guest running in VirtualBox. Unfortunately. then you can install putty from the repositories.work at all in VirtualBox. I will show you how to do it as cleanly as possible. Once you have your terminal installed.Don’t donwload all of CCS. the workaround for this setup is the most clumsy of the options. If you do not see it.. and in the prompt following. it should appear under the ‘Ports’ section. select ‘Browse my computer for driver software’ 4 In the textbox on the next page. Wait a few seconds. and should be assigned a COM port (mine is COM4 for example) 6 Download and install Tera Term 7 Open Tera Term and under the ‘Setup’ menu select ‘Serial’ 1 Set the COM port to match what showed in the Device Manager 2 Set the baud rate to 9600 3 Set data to 8 bit 4 Set parity to none 5 Set stop bits to 1 6 Set flow control to none 8 Save this setup as default by selecting ‘Save Setup’ under the ‘Setup’ menu You should now have serial access and see the menu print out in Tera Term. plug it back in and open Tera Term. and look for MSP430 Application UART. You should have serial access again. Option 1: Running in a VM with Windows host using Tera Term in Windows for serial If you have been following these tutorials from the beginning. The other thing I don’t like about this option is that you have to install drivers on Windows. reset the device using S1 or press enter a few times. you will lose access to the serial port. make sure mspdebug is closed. so close Tera Term first. you may have set up your environment as I have. you can plug in the Launchpad and open up /dev/ttyACM0 (or whatever port yours came up as). program debug etc. If you want to go back to serial. 1 Download the MSPWare package from TI’s website. minicom is my serial terminal of choice. Minicom is all command line. different setups etc… with no luck. I’m also not the biggest fan because I prefer minicom (and Linux) over Tera Term. Install the package. There are few options/workarounds which worked for me and you can decide which is best for you. If you choose to use minicom and are having problems setting it up. Now if . and unplug the Launchpad from the USB port. just MSPWare. Option 2: Linux host environment If you are following along with a Linux host. type in C:timspMSPWare_2_00_00_41examplesboardsMSP-EXP430G2MSP-EXP430G2 Software ExamplesDrivers and click next 5 Once the driver is installed. I tried for days recompiling kernel modules. so if you are not comfortable with that. but it is fairly reliable nonetheless. It should be under ‘Other Devices’ since Windows can’t find the driver 3 Right click and select ‘Update Driver Software’.

making debugging difficult. You should be able to program and debug. If you want to go back to serial. The clock is always driven by the master. Select this option and enter a frequency of 2Hz. Of the two signals on an I2C bus. they will not physically damage each other. In the next lesson. the open drain output pulls the line low. This is especially useful in multi-master mode – which is defined by the standard – when there are multiple masters communicating with the same or different slaves on the same bus. Stop and restart the blinking. close minicom and open mspdebug. Option 3: Use an external UART to USB converter The pitfall with both of the previous options is that you cannot use mspdebug and access the menu at the same time. because higher speeds are obviously desirable. fast mode was introduced to increase the supported rate up to 400kHz. For it to take effect. You should see the LED blink twice as fast. reset the device and take a look at the menu. This design has the advantage that it can support bus arbitration without the chance of bus contention at the electrical level. as well as the PCB design and layout. Bus arbitration (which master has control of the bus) is supported by the physical interface using the open drain design. we will look at improving our UART driver to handle receiving characters even when the CPU is busy doing other stuff. The disadvantage however. wait a few seconds and plug it back in again before opening minicom. and connect the device straight onto the headers using some jumper cables. When a device drives either of the lines.3V compatible version of the MAX232 – see the bread boarded picture from above). From the code described earlier. while the data is bidirectional. In other words. if two devices are driving the line. is that the the bus speed is limited especially over distance and across multiple devices (the limiting factor is in fact capacitance – max 400pF). However. one is the clock (SCL) and the other the data (SDA). Most devices support these two standard modes. We have only one option for now (we will add to this in the future). meaning it can be driven by either the master or the slave. There are higher speed modes as well. Now select the menu option again and change the frequency to 4Hz. Both signals are open drain and therefore high impedance (think open circuit) unless driven. Therefore. you must close minicom. but the speed of the bus is determined by the slowest device on the bus. unplug the device. however it is ideal to have this capability. Almost all I2C devices will support this rate. With a UART to USB. To achieve this.you want to use the debugger. the speed originally specified in the I2C bus standard was 100kHz. This may not be an issue for now since the code provided should work without modification. A pull-up resistor is required on each line to pull it high in the idle state. you can simply remove the jumpers from the Launchpad for the TX and RX lines. . Testing the UART Now that you have your device and PC all set up for UART. we know that this only sets a variable containing the timer period. which will set the frequency of the blinking LED. you can use a UART to USB converter (this one from Sparkfun is cheap and easy to use) or serial to USB converter with the MAX3232 (the 3. you must use the push button to start the blinking.

Other times the unshifted address may be provided. Conversely. I2C-bus specification and user manual (UM10204) It is important to remember that the address of a device is sometimes provided already shifted left by one while the hardware may expect the unshifted address. a transaction is ended with a STOP condition. the rising edge of SDA is the hardware trigger for the STOP condition.3V device. A STOP condition can be issued at any time by the master. The device address is the first 7 bits (the most significant bits) while R/W is always bit 0. This makes the bus flexible in the sense that devices powered with 5V can run I2C at 5V. You cannot connect a 5V I2C bus to a 3. Now all the devices on the bus are listening.The voltage levels of I2C are not defined by the specification. while devices that run on 3.3V. The I2C protocol is defined by a set of conditions which frame a transaction. For this scenario the design would require a voltage level shifter on the I2C bus between the two devices.3V can communicate at 3. . I2C-bus specification and user manual (UM10204) Immediately after the START. In this case. the master must send a single byte which comprises of the device address and the read/write (R/W) bit. This is the most often error when a device does not respond. Voltage level shifters specifically designed for I2C applications are available. The falling edge of SDA is the hardware trigger for the START condition. where the master leaves SCL idle and releases SDA so it goes high as well. Instead it defines a high or low symbol relative to Vcc. The start of a transmission always begins with a START condition during which the master leaves SCL idle (high) while pulling SDA low. The pitfall comes when devices are powered at different levels and need to communicate with each other. but the hardware expects the shifted address.

Keep in mind that memory devices are often advertised in terms of bits and not bytes. If it is set to read (high) the slave will be transmitting. I2C-bus specification and user manual (UM10204) For the case when the device address is transmitted. Next comes the data. The transmitting device must stop transmitting upon receiving the NACK. the slave device with the matching address should respond with an ACK. If it is low. while the other device reads the state of the line. All subsequent data bytes are transmitted by the slave to the master. After each byte is sent or received. The address is transmitted most significant bit first. it is interpreted as an ACK. manufacturing date. The standard defines three transaction formats: ▪ Master transmitter slave receiver – the master sends the first byte with the R/W bit cleared (write). It determines the direction of transmission of the subsequent bytes. etc… The specific EEPROM we will be using is the Atmel AT24C02D. All subsequent data bytes are transmitted by the master and received by the slave. while if it set to write (low) the master will be transmitting. lets take a look at the slave device we will be communicating with. there is what is called a repeated START condition which is exactly the same as a START but not preceded by a STOP. the 9th clock cycle is reserved for the ACK/NACK (acknowledge/not acknowledge) symbol. if it left idle a NACK. the device pulls SDA low on the 9th clock cycle. so 2Kb = 2 kilobits. hardware revision. Instead of the STOP. If there is no device on the bus with a matching address. To signal an ACK. The slave device Before we move onto implementing the driver. A very commonly used I2C device is the EEPROM. ▪ Combined format – effectively format 1 + 2 consecutively with no STOP condition in the middle. Once the number of bytes requested has been written or read. which is (2048/8) 512 bytes – not that that much memory. an ACK can be driven by the either the master or the slave.The R/W bit indicates to the slave whether the master will be – well – reading or writing to/from the slave device. Even though it has an endurance of 1 million write cycles (meaning the device is guaranteed to be able to perform at least 1 million writes before . A single bit is transmitted on SDA for each clock cycle on SCL – therefore transmitting a byte takes 8 clock cycles. the master completes the transaction with the STOP condition. ▪ Master receiver slave transmitter – the master sends the first byte with the R/W bit set (read). the master will receive a NACK and should end the transaction. Typically a slave should NACK any data byte if it unable to accept any more or if it is busy. EEPROMs are typically used to store small amounts of data which don’t change very often such as serial number. Depending on the transaction. The data transmitted or received can be as small as one byte up to as many bytes supported by the slave device. a 2Kb EEPROM with up to 1 million write cycles and can operate at speeds up to 1MHz.

These pins are internally pulled to ground inside the device. we can see the package description and pin layout for the DIP. they require pull-up resistors. Pins 5 and 6 are the I2C lines – these will be connected to the MSP430’s SCL and SDA pins which we will configure in software. so that will be connected that to the ground rail. Taking a look at the datasheet.7kOhms for this the breadboard circuit.e. we will tie this pin to ground. Pin 7 is the write protect signal. writing to an EEPROM is quite slow so it is not really intended to be used to store data at runtime. will be connected to the Vcc rail. the designer can ensure the contents cannot be overwritten by pulling the write protect line high in hardware. One of the reasons I choose this device is because it is available in a DIP package so it is easy to breadboard.failing). Since we want to be able to write to the EEPROM. With all this connected so far.7kOhms to 10kOhms. These pins allow the designer to change the lower three bits of the address so up to eight of these devices can coexist on the same bus. Vcc. Because EEPROMs are often used to store static data that should never change (i. Finally pin 8. Typical values for pull-up resistors on an I2C bus range from 4. the breadboard looks like this: . so we can leave them not connected unless you want to change the address (although Atmel does recommend connecting them when possible). a serial number or a MAC address). all the devices would respond to every request – obviously not desirable. Let’s say a hardware designer needs three EEPROMs on the same I2C bus. We will use 4. as we learned previously. Atmel AT24C02D Datasheet (Atmel-8871E) Pins 1 – 3 are the customizable address bits A0 – A2 for the I2C device address. Pin 4 is the ground pin. However. The top 4 bits (A3 – A6) are hard coded in the device (we’ll see what these are shortly). If they all had the same address.

Upon receiving this byte. This will be important when we implement the driver.Now let’s take a look at the device address in section 7 of the EEPROM datasheet. The master device can either write to or read from the EEPROM. the . This is the diagram provided: Atmel AT24C02D Datasheet (Atmel-8871E) So your initial thought might be that we have to address the device as 0xA0. Therefore it should be shifted right by 1 making it 0b1010000 = 0x50. Since we left pins A0 – A2 floating. it is not technically correct. Although this is what the datasheet implies and what is physically transmitted. Writing to the EEPROM can take two forms: byte write and page write. Most I2C EEPROMs typically support the same types of transactions. the lower 3 bits of the address will be 0b000. Both require that the first data byte is the address to write to. This address is stored in the EEPROMs internal current address register. The actual device address should not include the R/W bit. if the master wants to write to address 0x10. For example. The next data byte is the actual value to write to the EEPROM. The datasheet specifies that the upper 4 bits will be 0xA (0b1010). the first data byte will be 0x10.

Note that both byte write and page writes are of the transaction format 1 – master transmitter slave receiver. instead of the address wrapping around a single page. Like writes. If the master than sends a STOP. After one byte is read. Atmel AT24C02D Datasheet (Atmel-8871E) A page write need not transmit a full page however. it wraps across the whole device memory space. random address read and sequential read.EEPROM should respond with an ACK. Atmel AT24C02D Datasheet (Atmel-8871E) The master also has the option to continue sending data to the slave. In the case of the AT24C02D. only the 3 least significant bits in the address will increment. This is called a page write. each byte that is read increments the current address. Reading data back from the EEPROM can be performed using one of 3 operations: current address read. . the page size is 8 bytes. It is therefore important to limit each transaction to a maximum of 8 bytes and then a new transaction with the updated address (incremented by 8 each time) be initiated. this transaction is a byte write. It is up to a page of data that can be written in a single transaction. However. if the master issues a NACK it is done reading data and it should subsequently send a STOP. If the master sends more than 8 bytes. until either the EEPROM responds with a NACK – indicating it is busy – or a full page is written. those bits will wrap around and the first address will be overwritten. the current address register in the EEPROM is incremented automatically. The current address read makes use of the EEPROM’s current address register to read the next address in the device. However. After each byte is received.

This current address read becomes a sequential read. Atmel AT24C02D Datasheet (Atmel-8871E) Notice that both current address read and sequential read (when following a current address read) are in the transaction format 2 – master receiver slave transmitter. it will be expecting more data. changes direction to read and reads a single byte from the EEPROM. if the master responds with an ACK after the first data byte. . The master will continue to ACK each data byte until it is done receiving the number of bytes it requires. Then it invokes a repeated START condition. First the master transmits the device address with the R/W bit set to write and one data byte containing the address to read from.Atmel AT24C02D Datasheet (Atmel-8871E) However. On the last data byte it must respond with a NACK and then a STOP condition. But what if we need to read from a specific address – which is most often the case? This is where message format 3 – combined format – comes into play with the random address read. which basically means that multiple bytes are read out of the slave device.

UCBxCTL0. Those fields have been marked as such. The USCI_Bx module implements I2C and SPI. we looked at USCI_Ax which implements UART and SPI. instead of NACKing the first byte. USCI_Bx Control Register 0. I2C with the USCI Module On the MSP430. specifically those fields which apply to I2C. the peripheral which implements I2C is the USCI module. contains the configuration for the protocol. Just like with the current address read. Let us review the USCI module registers.Atmel AT24C02D Datasheet (Atmel-8871E) Sequential mode can also be applied to the random address read. which we will cover in another lesson. TI MSP430x2xx Family Reference Manual (SLAU144J) BIT FIELD DESCRIPTION 7 UCA10 Address mode (slave only) 0b0: 7 bit address mode 0b1: 10 bit address mode 6 UCSLA10 Slave address mode 0b0: 7 bit address mode 0b1: 10 bit address mode 5 UUCMM Multi-master environment (slave only) 0b0: Single master environment 0b1: Multi-master environment 3 UCMST Master/slave mode 0b0: Slave mode 0b1: Master mode . Note that the same module is used to configure the MSP430 as a slave device. In previous lessons. The first configurations register. the master continues to ACK until it has read the desired number of bytes.

TI MSP430x2xx Family Reference Manual (SLAU144J) BIT FIELD DESCRIPTION 6 UCSCLLOW SCL line held low 0b0: SCL not held low 0b1: SCL held low 5 UCGC General call address received 0b0: No general call address received . configures the USCI module in terms of clocking and is used by the driver to generate the START/STOP/ACK conditions. TI MSP430x2xx Family Reference Manual (SLAU144J) BIT FIELD DESCRIPTION 7-6 UCSSELx USCI clock source select 0b00: UCLK external clock source 0b01: ACLK 0b10: SMCLK 0b11: SMCLK 4 UCTR Transmitter/receiver mode – sets R/W 0b0: Receiver (read from the slave) 0b1: Transmitter (write to the slave) 3 UCTXNACK Transmit a NACK 0b0: Send ACK 0b1: Send NACK 2 UCTXSTP Generate a STOP condition 0b0: No STOP generated 0b1: STOP generated (automatically cleared upon completion) 1 UCTXSTT Generate a START condition 0b0: No START generated 0b1: START generated (automatically cleared upon completion) 0 UCSWRST USCI module software reset 0b0: USCI operational – not in reset 0b1: Reset USCI module The UCBxSTAT register contains the status of the module. USCI_Bx Control Register 1.2-1 UCMODEx USCI mode 0b00: 3-pin SPI (not valid for I2C) 0b01: 4-pin SPI STE=1 (not valid for I2C) 0b10: 4-pin SPI STE=0 (not valid for I2C) 0b11: I2C 0 UCSYNC Synchronous/Asynchronous mode 0b0: Asynchronous (Invalid for I2C) 0b1: Synchronous (SPI/I2C) The second control register. UCBxCTL1.

equivalent fields are in registers UC1IE and UC1IFG respectively. When UCAxRXBUF is read by software. the low and high bytes which form the prescaler value. Well see how to configure these later in the lesson. Similarly. UCBxRXBUF and UCBxTXBUF respectively. This also clears UCAxTXIFG (transmit complete interrupt flag). To receive and transmit data there are two 8-bit registers. The UCBxI2CSA is the slave address register. these fields are only for USCI_B0. If there is a second USCI_B module (USCI_B1). To send data (not including the device address byte). it is stored in UCAxRXBUF and UCAxRXIFG (receive interrupt flag) is set. Once the transmission is complete. TI MSP430x2xx Family Reference Manual (SLAU144J) BIT FIELD DESCRIPTION 3 UCB0TXIFG USCI_B0 transmit complete interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending 2 UCB0RXIFG USCI_B0 receive interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending Note that the undefined bits may be used by other modules depending on the specific device. UCAxTXIFG will be set. The data is held in this register until it is read by software. 0b1: A general call address was received 4 UCBBUSY Busy busy 0b0: Bus free – no transaction in progress 0b1: Bus busy – transaction in progress 3 UCNACKIFG Not acknowledged interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending 2 UCSTPIFG STOP condition interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending 1 UCSTTIFG START condition interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending 0 UCALIFG Arbitration lost interrupt flag 0b0: No interrupt pending 0b1: Interrupt pending The SFR IFG2 contains the interrupt status bits for the USCI module. . See the device data-sheet for more information. Next we have the two baud rate control registers UCBxBR0 and UCBxBR1. This is where the driver writes the address of the slave and the hardware will automatically shift the address left by one bit to accommodate the R/W bit. Also. data is written to UCBxTXBUF. when data is received on line. UCAxRXIFG is cleared.

6 is and P1.Registers IE2 and UCBxI2COA. 1 int i2c_init(void) 2 { 3 /* Ensure USCI_B0 is in reset before configuring */ 4 UCB0CTL1 = UCSWRST. are only required for interrupt based drivers and slave configuration respectively and therefore will not be covered in this lesson. The pins need to be configured as well.c. The baud rate registers act a divider. we only need to set the low byte UCB0BR0 to 10. Next the clock source is selected to be SMCLK by setting UCSSELx to 0b10 in UCB0CTL1.7 respectively. . The initialization function should be called from board_init along with the rest of the hardware initialization. This function will be responsible for configuring the features which will not change during runtime. 14 UCB0BR1 = 0. Based on our clock module configuration. UCMST to 0b1 (master) and UCSYNC to 0b1 (I2C is a synchronous protocol) in the UCB0CTL0 register. 20 } The module is setup for master mode I2C by setting UCMODEx to 0b11 (I2C). etc…) but it will suffice for learning the basics. SCL and SCK are located on P1. Therefore. Implementing the driver Now that we have a high level understanding of the USCI module register set in I2C mode. The I2C driver will be quite simple – it will not use interrupts at this point. 8 9 /** 10 * Configure the baud rate registers for 100kHz when sourcing 11 * where SMCLK = 1MHz 12 */ 13 UCB0BR0 = 10. this means the USCI module will be running at 1MHz. 15 16 /* Take USCI_B0 out of reset and source clock from SMCLK */ 17 UCB0CTL1 = UCSSEL_2. First we will start off with a simple initialization routine which will live in a new source file src/i2c. We want the I2C bus to run at the standard 100kHz. Everything else can remain at the default values as they are sufficient for our use case. so the divider value must be 1MHz / 100kHz = 1000000 / 100000 = 10. only polling – not the best implementation for several reasons covered previously (blocking. finally we can take the USCI module out of reset by clearing UCSWRST in UCB0CTL1. 18 19 return 0. Now that everything is set up. With the source clock frequency configured. let’s get coding. 5 6 /* Set USCI_B0 to master mode I2C mode */ 7 UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC. power consumption. From the pinout in the device datasheet for the MSP430G2553. we can now setup the baud rate registers.

14 15 /* Initialize UART to 9600 baud */ 16 config. The transfer function should require the device structure as a parameter so that it can support multiple slave devices on the same bus. 4 }. 8 P1SEL2 |= BIT6 + BIT7.7 for I2C */ 7 P1SEL |= BIT6 + BIT7.] 2 /* Configure P1.h. 20 } 21 22 if (i2c_init() != 0) { 23 while (1).2 for UART (USCI_A0) */ 3 P1SEL |= 0x6. Recall the reason we put these pin configurations here and not in the driver is to help isolate the driver implementation from the board specific configuration. 24 } In a new header file include/i2c.6 and P1... 12 13 watchdog_enable().baud = 9600. It . 9 10 /* Global interrupt enable */ 11 __enable_interrupt().1 and P1. Next we will write the transfer function. 1 struct i2c_device 2 { 3 uint8_t address. we will define the I2C device structure which for now only consists of the device address of the slave device. 5 6 /* Configure P1. Now board_init should look like this: 1 [. In the future it may include other device specific parameters. 4 P1SEL2 |= 0x6. 17 18 if (uart_init(&config) != 0) { 19 while (1).TI MSP430G2x53 Datasheet (SLAS735J ) These two pins must be configured to work with the USCI block by setting the applicable bits in P1SEL and P1SEL2 high.

7 }. Let’s quickly verify how this covers all three I2C transaction formats. (uint8_t *) data->rx_buf. 6 size_t rx_len. one to transmit and another to receive. The actual writing of the buffer to the hardware is broken out into a separate function for the sake of keeping functions small and readable. Now the transfer function only takes two parameters. struct i2c_data *da 2 { 3 int err = 0.should also be able to handle all three transaction formats so it will require two buffers. the i2c_device structure and i2c_data structure. 5 void *rx_buf. and if there are no errors. It will also need to know the length of these buffers. Instead of making this function take a huge argument list. then the transaction is complete and the master should issue the STOP condition by setting UCTXSTP in the UCB0CTL1 register. then it’s time to see if the master needs to read any data from the slave. (const uint8_t *) data->tx_buf. The following transactions will therefore be directed at this device. If so. 22 } The function begins by setting the slave device address in the UCB0I2CSA register. . If there are bytes to transmit. 4 5 /* Set the slave device address */ 6 UCB0I2CSA = dev->address. If there are no bytes to receive. we will define another structure – i2c_data – in i2c. so check the size of the transmit buffer is greater than zero – if so transmit the buffer. these are sent first. then call the receive function. To support all three I2C transaction formats we need to first consider the transmit buffer. data->rx_len 16 } else { 17 /* No bytes to receive send the stop condition */ 18 UCB0CTL1 |= UCTXSTP. Once the transmit is complete. 4 size_t tx_len. 1 int i2c_transfer(const struct i2c_device *dev. data- 11 } 12 13 /* Receive data is there is any */ 14 if ((err == 0) && (data->rx_len > 0)) { 15 err = _receive(dev.h which will encapsulate both transmit and receive buffers and their respective sizes. 1 struct i2c_data 2 { 3 const void *tx_buf. 19 } 20 21 return err. 7 8 /* Transmit data is there is any */ 9 if (data->tx_len > 0) { 10 err = _transmit(dev.

4 IGNORE(dev). we can see that this function will provide the flexibility required to support all three I2C formats and therefore should support any I2C slave device. Therefore the transmit section of the function will be skipped. The length of the receive buffer should be greater than zero and therefore the master will read that number of bytes from the slave and then the STOP condition will be set. 8 9 /* Wait for the start condition to be sent and ready to transm 10 while ((UCB0CTL1 & UCTXSTT) && ((IFG2 & UCB0TXIFG) == 0)). 1 static int _transmit(const struct i2c_device *dev. 11 12 /* Check for ACK */ 13 err = _check_ack(dev). Start by transmitting the required number of bytes. transmit the data */ 16 while ((err == 0) && (nbytes > 0)) { 17 UCB0TXBUF = *buf.Master transmitter slave receiver: The transmit buffer will have data and therefore the length should be non-zero. If no errors have occurred. 20 if (err < 0) { 21 break. 26 nbytes--. 30 } . the STOP condition will be set. The receive buffer will have a length of zero so master does not receive any data from the slave. Once that is complete. 22 } 23 } 24 25 buf++. Based on this quick analysis. 14 15 /* If no error and bytes left to send. Combined format: In this case both the transmit and receive buffers are greater than zero. 5 6 /* Send the start condition */ 7 UCB0CTL1 |= UCTR | UCTXSTT. Now let’s take a look at how the driver transmits data from the master to the slave. a repeated START condition will be issued and the master will receive data from the slave. 18 while ((IFG2 & UCB0TXIFG) == 0) { 19 err = _check_ack(dev). Master receiver slave transmitter: The transmit buffer will have a length of zero. 27 } 28 29 return err. const uint8_t * 2 { 3 int err = 0. Data will be transmitted to the slave. Therefore immediately after the transmit is complete the STOP condition will be set.

Again we wait until the transmit interrupt flag is set and check for the ACK. the UCTR bit needs to be set as well. Since the master is transmitting data to the slave. The master must receive an ACK for every data byte before transmitting the next one. si 2 { 3 int err = 0. 12 13 /* 14 * If there is only one byte to receive. 1 static int _receive(const struct i2c_device *dev. 4 IGNORE(dev). 9 10 /* Wait for the start condition to be sent */ 11 while (UCB0CTL1 & UCTXSTT). . The hardware will now set the START condition and send the first byte with with I2C device address and R/W bit after which the UCTXSTT bit will be cleared and the transmit interrupt flag UCB0TXIFG in IFG2 set. uint8_t *buf. let’s take a look at how the master receives data from the slave device. A slave device may NACK additional data if it busy so receiving a NACK would be an indicator to the master to stop transmitting. which puts the USCI module in transmit mode. 5 6 /* Send the start and wait */ 7 UCB0CTL1 &= ~UCTR. This cycle is repeated until all the data has been transmitted (or the transaction is forced to stop by a NACK).The transmission begins by setting the START condition. we must check to make sure a slave acknowledged the initial byte. 19 } 20 21 /* Check for ACK */ 22 err = _check_ack(dev). Before transmitting the data however. in which case a there should be a repeated START condition and not a STOP condition. then it is safe to load the first data byte into the transmit buffer. 23 24 /* If no error and bytes left to receive. 28 29 *buf = UCB0RXBUF. Notice at the end of the transmit function that we do not send a STOP condition because there may be data to receive. This is a common check. then set the stop 15 * bit as soon as start condition has been sent 16 */ 17 if (nbytes == 1) { 18 UCB0CTL1 |= UCTXSTP. this is done by setting UCTXSTT in the UCB0CTL1 register. On the MSP430. so it has been broken out into its own function which we’ll take a look at in more detail shortly. 8 UCB0CTL1 |= UCTXSTT. If the master received an ACK from the slave device. Next. receive the data */ 25 while ((err == 0) && (nbytes > 0)) { 26 /* Wait for the data */ 27 while ((IFG2 & UCB0RXIFG) == 0).

putting the USCI module in receive mode. must be set. 1 static int _check_ack(const struct i2c_device *dev) 2 { 3 int err = 0. UCTXSTT is set to start the transaction and once the first byte is sent. Once the flag is set the data received is in the UCB0RXBUF register and can be read out. The slave will still ACK the I2C device address byte. 10 11 /* Clear the interrupt flag */ 12 UCB0STAT &= ~UCNACKIFG. 43 } Receiving data requires the master to send the START condition and slave device address byte but this time with the R/W bit cleared by clearing UCTR. Finally. 4 IGNORE(dev). 32 33 /* 34 * If there is only one byte left to receive 35 * send the stop condition 36 */ 37 if (nbytes == 1) { 38 UCB0CTL1 |= UCTXSTP. 31 nbytes--. as soon as the first byte has finished transmitting. 13 14 /* Set the error code */ 15 err = -1. In master receive mode setting this bit sends the NACK and then issues the STOP condition. If the receive buffer is only one byte. The master must NACK the last data byte it wants to receive.30 buf++. Otherwise. the stop bit. This is repeated for all data bytes until there is only one left to receive. so before receiving it we must set UCTXSTP. Assuming the ACK was received. so this must be verified by the master. but in the case of master receive mode the ACK is driven by the master rather than the slave. 5 6 /* Check for ACK */ 7 if (UCB0STAT & UCNACKIFG) { 8 /* Stop the I2C transmission */ 9 UCB0CTL1 |= UCTXSTP. the slave does not know to stop sending data and a bus error or device error may result. 16 } . each byte triggering the receive interrupt flag. let’s take a look at how to handle the N/ACK from the slave device. The master must NACK the last data byte and then issue a STOP condition. UCTXSTT will be cleared by the hardware. UCTXSTP in UCB0CTL1. Now the slave will begin sending data. 39 } 40 } 41 42 return err. the slave device will begin sending over data.

Vcc and ground are straightforward – they are simply connected to the Vcc and ground pins on the LaunchPad.7 were configured for SCL and SDA respectively in board. 4 struct i2c_device dev. so those pins can be connected to the EEPROM pins 6 and 5 on the breadboard. it must send a STOP condition and should clear the interrupt flag.17 18 return err. 1 static int eeprom_read(void) 2 { 3 int err. UCNACKIFG in the status register UCB0STAT. It could be the address does not match.6 and P1. it should abort the transaction. Lets connect the breadboard to the MSP430 LaunchPad. To test out our driver. Using the driver to write and read data Now that the driver is written. 8 . should be read.c. 5 struct i2c_data data. 7 uint8_t address. Pins P1. the NACK interrupt flag field. 6 uint8_t rx_data[1]. we will create two new menu options to read and write a single byte to the EEPROM. Therefore. or the slave can no longer receive any data. When the master receives a NACK. we can use it to store data to the EEPROM. Currently they only support reading and writing one byte of data but they could be extended to ask the user for a length. To check for a NACK. or you can modify the code to change the size of the buffers. 19 } There are a few conditions in which the master might receive a NACK as we have seen.

48 } In both cases the user is asked to enter the address.tx_buf = write_cmd. 44 45 err = i2c_transfer(&dev. which has been defined with one element. &data). 16 data. the size of this array can be modified.tx_len = sizeof(address). The i2c_transfer function is called and and the .rx_buf = (uint8_t *) rx_data.rx_len = ARRAY_SIZE(rx_data). which is standard for this device (other EEPROMs or I2C devices that have a bigger address space may require more than 1 byte for the address). 24 } 25 26 return err. 10 11 address = (uint8_t) menu_read_uint("Enter the address to read: 12 13 data.9 dev. 46 47 return err. 35 36 dev. 39 write_cmd[1] = menu_read_uint("Enter the data to write: "). &data). 32 struct i2c_device dev. The read function points the transmit buffer to the address and sets the length to 1 byte.tx_len = ARRAY_SIZE(write_cmd). 22 uart_puts(_int_to_ascii(rx_data[0])). If you want to increase the number of bytes read. The receive buffer points to the rx_data array. 40 41 data. 19 20 if (err == 0) { 21 uart_puts("\nData: "). 23 uart_putchar('\n').tx_buf = &address. 14 data. 42 data.rx_len = 0. 17 18 err = i2c_transfer(&dev. 33 struct i2c_data data. 43 data. 37 38 write_cmd[0] = menu_read_uint("Enter the address to write: ").address = 0x50. 15 data. 34 uint8_t write_cmd[2].address = 0x50. 27 } 28 29 static int eeprom_write(void) 30 { 31 int err.

the SDA line toggles to 0b00000111 = 0x7. Then the address to read from is transmitted. In this case. the master can continue clocking in the byte. When it is done.received data is printed out to the serial port. The transaction ends with the STOP condition and both lines return to idle. The blue trace is SCL and the yellow trace SDA. For example. In this case the data at address 0x7 was 0xFF – the ‘erased’ value of an EEPROM. at the end of the first image. Over the next 8 clock cycles. This is called clock stretching and it is implemented by the hardware to delay the next byte in the transaction. the SDA line is low. both SDA and SCL are released high and the repeated START condition is issued. We can see the first byte being transmitted is 0xA1 ((device address << 1) | write = (0x50 << 1) | 0x1 = 0xA1). try to read the data at address 0x7 – here is a screenshot of the I2C transaction from an oscilloscope. indicating that the EEPROM acknowledged the first byte. Since a read is a combined format transaction. the EEPROM is saying ‘wait for me to retrieve the data!’. However. The transmit buffer is pointed to the write_cmd array which has a length of 2 bytes. Again on the 9th clock cycle the EEPROM acknowledges. one . The EEPROM acknowledges once more and the next 8 clock cycles it transmits the data byte back to the master. you can see both lines are held low for quite some time. Now the first byte is 0xA0 ((device address) << 1 | read = (0x50 << 1) | 0 = 0xA0). The write function is similar except that the user is also prompted for the value to write. On the 9th clock cycle.

for the address and the other for the data. Share this: . the SDA line toggles to 0b00000111 = 0x7. indicating that the EEPROM acknowledged the first byte. this could be increased in size to write more data. Over the next 8 clock cycles. the SDA line is low. the transaction will look like this: The first byte being transmitted is 0xA1 ((device address << 1) | write = (0x50 << 1) | 0x1 = 0xA1). Then the address to write is transmitted. On the 9th clock cycle. driver and hardware setup. Again. The receive buffer isn’t set but the length must be set to 0 to indicate to the driver there are no bytes to receive. This test code is not really how an EEPROM would be accessed in practice but at least we can test our interface. In the next tutorial we will look more at reading and writing from the EEPROM and demonstrate some real-life use cases. Again on the 9th clock cycle the EEPROM acknowledges and then the master starts to transmit the data and we can see the SDA line toggle to 0b00110111 = 55. The transaction once again ends with the STOP condition and both lines return to idle. If I now write the value 55 (0x37) to address 0x7.