ECE 3567 Microcontrollers Lab Lecture #2 – The Microcontroller & Embedded C Programming

Autumn 2019 Dr. Gregg Chapman

1 Numbering The Hexadecimal Number System. Hexadecimal Values of Positions (Used to read or write register values)

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 1 1 0 0 0 0 0 0 0xC0 0 0 0 0 0 0 1 1 0x03 1 1 0 0 0 1 1 1 0xC7 0 0 0 0 1 1 1 1 0x0F 1 1 1 1 0 0 0 0 0xF0 0 0 0 0 0 0 0 0 0x00 0 1 0 1 0 1 0 1 0x55 1 0 1 0 1 0 1 0 0xAA 1 1 1 1 1 1 1 1 0xFF Registers –

WE USE: 7 6 5 4 3 2 1 0 Hexadecimal Values of Bit Positions Don’t get Bit by Bit Numbering

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0 0 0 0 0 0 0 1 0x01 0 0 0 0 0 0 1 0 0x02 0 0 0 0 0 1 0 0 0x04 0 0 0 0 1 0 0 0 0x08 0 0 0 1 0 0 0 0 0x10 0 0 1 0 0 0 0 0 0x20 0 1 0 0 0 0 0 0 0x40 1 0 0 0 0 0 0 0 0x80 Register Terminology

• Note that each 4-bit can be converted to a single Hexadecimal character.

• Register contents are always expressed in hexadecimal values. Registers

0000 0100 1110 0000

0x0 , or 0h 0x4 , or 4h 0xE , or Eh 0x0 , or 0h

• Example of 4-bit binary values converted to a single Hexadecimal character.

This 16-bit register value would be shown as: 0x04E0 Data Types C Programming Language

! ! ! ! √ √ X X √ √ X X X Data Types Processor Dependent!!

MSP430 Series Microcontrollers

√ √ ! ! √ √ Variable Declaration Modifiers

• const – single value at runtime and cannot be altered • volatile – value can be altered anywhere in code (global) • extern – declaration is in another file, but can be used in current file • static – value can be altered only within the current file

Examples:

volatile unsigned int delay; extern volatile unsigned char i, j, k; static int counter; const int x = 123; Constant Declarations

There are “constant” variables:

const int x = 0x1234;

Constant numbers are usually defined with the compiler directive:

#define RED 0x11 C Operations (for reference) C Operations C Operations C Operations MOST Important Operators in Embedded C Programming !

Used to CLEAR

Used to TOGGLE Bits

Used to SET Bits

Used to INVERT ALL Bits HOW TO SET A BIT IN A REGISTER

In C programming, the | character is a BITWISE OR

NOTE THAT: 0 | 0 = 0 0 | 1 = 1 1 | 0 = 1 1 | 1 = 1

By BITWISE ORing a register with 1s in the bits you want to SET, and 0s in the bits that you want to preserve,

YOU ONLY SET THE BITS AT LOCATIONS WHERE THERE ARE A ONES IN THE USED TO OPERATE ON THE REGISTER HOW TO SET A BIT IN A REGISTER EXAMPLE 1:

RESIGTER is: 0000 0000 SET Bit 4

REGISTER |= 0x10; 0x10 = 0001 0000 (0000 0000) | (0001 0000) = (0001 0000) You have SET Bit 4 HOW TO SET A BIT IN A REGISTER EXAMPLE 2:

RESIGTER is: 1010 1010 SET Bit 2

REGISTER |= 0x04; 0x04 = 0000 0100 (1010 1010) | (0000 0100) = (1010 1110) You have SET Bit 2 HOW TO SET A BIT IN A REGISTER EXAMPLE 3:

RESIGTER is: ???? ???? SET Bit 7

REGISTER |= 0x80; 0x80 = 1000 0000 (???? ????) | (1000 0000) = (1??? ????)

You have insured that Bit 7 is SET regardless of the other bit values. HOW TO SET A BIT IN A REGISTER EXAMPLE 4:

RESIGTER is: 1111 0000 SET Bits 0 and 1

REGISTER |= 0x03; 0x03 = 0000 0011 (1111 0000) | (0000 0011) = (1111 0011)

You have SET bits 0 and 1. HOW TO CLEAR A BIT IN A REGISTER

In C programming, the & character is a BITWISE AND

NOTE THAT: 0 & 1 = 0 1 & 1 = 1

By BITWISE ANDing a register with 1s in the bits you want to preserve, and 0s in the bits that you want to clear,

YOU ONLY CLEAR THE BITS AT LOCATIONS WHERE THERE ARE A ZEROS IN THE BYTE USED TO OPERATE ON THE REGISTER HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 1:

RESIGTER is: 1111 1111 Clear Bit 4

REGISTER &= 0xEF; 0xEF = 1110 1111 (1111 1111) & (1110 1111) = (1110 1111)

You have CLEARED Bit 4 HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 2:

RESIGTER is: 1010 1010 Clear Bit 3

REGISTER &= 0xF7; 0xF7 = 1111 0111 (1010 1010) & (1111 0111) = (1010 0010)

You have CLEARED Bit 3 HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 3:

RESIGTER is: 0000 0000 Clear Bit 0

REGISTER &= 0xFE; 0xFE = 1111 1110 (0000 0000) & (1111 1110) = (0000 0000)

You have insured that Bit 0 is cleared HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 4:

RESIGTER is: ???? ???? Clear Bit 7

REGISTER &= 0x7F; 0x7F = 0111 1111 (???? ????) & (0111 1111) = (0??? ????)

You have insured that bit 7 is cleared, regardless of the other bit values HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 5:

RESIGTER is: 1111 0000 Clear Bits 6 and 5

REGISTER &= 0x9F; 0x9F = 1001 1111 (1111 0000) & (1001 1111) = (1001 0000)

You have cleared Bits 6 and 5 HOW TO CLEAR A BIT IN A REGISTER EXAMPLE 6: Alternate Notation: RESIGTER is: 1111 0000 Clear Bits 6 and 5

REGISTER &= ~(BIT6 & BIT5); (BIT6 & BIT5) = (0100 0000) & (0010 0000) = 0110 0000 ~(BIT6 and BIT5) = 1001 1111

(1111 0000) & (1001 1111) = (1001 0000)

You have cleared bits 6 and 5 TI Method of Setting BIT Fields of Different Sizes

2*0x1000u DEFINES THE FIRST BIT OF THE FIELD TO BE USED #define BIT0 (0x0001) STEPS: #define BIT1 (0x0002) 1. Convert this number to BINARY #define BIT2 (0x0004) #define BIT3 (0x0008) 2. Place the number in the register with #define BIT4 (0x0010) the Least Significant Bit of the field #define BIT5 (0x0020) defined by this value (See Table) #define BIT6 (0x0040) #define BIT7 (0x0080) EXAMPLES: 0010 0000 0000 0000 #define BIT8 (0x0100) #define BIT9 (0x0200) 9*0x0020u: #define BIT10 (0x0400) 0000 0001 0010 0000 #define BIT11 (0x0800) #define BIT12 (0x1000) 7*0x2000u: #define BIT13 (0x2000) 1110 0000 0000 0000 #define BIT14 (0x4000) #define BIT15 (0x8000) 3*0x0040u: 0000 0000 1100 0000 Other Ways to Express the Bit Patterns

0000 1000 = 0x08 = BIT3 Because a #included header file msp430fr6989.h contains: This is HEX not Binary #define BIT0 (0x0001) 1111 0111 = 0xF7 = ~0x08 = ~BIT3 #define BIT1 (0x0002) #define BIT2 (0x0004) #define BIT3 (0x0008) #define BIT4 (0x0010) #define BIT5 (0x0020) #define BIT6 (0x0040) #define BIT7 (0x0080) #define BIT8 (0x0100) #define BIT9 (0x0200) #define BITA (0x0400) #define BITB (0x0800) #define BITC (0x1000) #define BITD (0x2000) #define BITE (0x4000) #define BITF (0x8000)

NOTE: msp430fr6989.h is #included in driverlib.h Toggling Bits

Compliment Bit 0 of register P1OUT

P1OUT ^= BIT0; // P1OUT XOR 0x0001 inverts BIT0 only

(NOTE: This is the Bitwise Exclusive OR operator: A ZERO retains the bit value of all the positions that are zero. A ONE INVERTS the positions that are 1. Since BIT0 is #defined as 0x0001 it is the only bit complimented. Pretty Cool (not to mention useful). a b XOR 0 0 0 0 1 1 1 0 1 1 1 0

31 Programming Compiler Directives (from Lecture #1)

• #include – append contents of the external file • #define – text substitution for a constant, can assign a numeric value • #pragma – directs compiler to ignore multiple declarations of the same entity • #ifdef – begin conditional compilation, paired with #endif • #ifndef – define only if it hasn’t already been defined • #typedef – defines an alias name or new type Function Prototypes

void funct1(); void funct1(void); void funct2(unsigned int); unsigned int funct3(void); unsigned int funct4(char); unsigned int funct5(char, unsigned int);

NOTE: I put all function prototypes in a separate header file called 3567.h Functions Called by Value Functions Called by Reference 2 Local Variables

• Declared inside functions

• Are not preserved when the execution returns form the function unless returned. void delay_cycles(unsigned int x) { unsigned int a; a = x; while (a >= 0) { a--; } return; } Function with Local Variables

NOTE: You can return a local variable if the function prototype has a returned parameter Loops in Embedded C Use while loops if possible

FOR LOOP WHILE LOOP

Also a do-while which tests at end. Not used much. if-else if-else or switch ? if-else if-else or switch ?

• Use switch statements, for more than 3 conditions AND the values are constants. The cross-compiler will make a jump table (which is way more efficient than multiple tests).

• Use if-else if-else if the cases are Boolean or logical expressions.

• In general, most cross-compilers are more efficient with switch.

• switch is much more readable (easy to understand), IMHO

• For a low number of cases, the difference is negligible. How to handle external delays of unknown duration

NEVER just wait: (assumes no Watchdog Timer)

TAOCTL0 |+= CCIE; // Enable process while(CCIF == 0); // CODE HANGS HERE if no event Value = TAOR; // Read Result How to handle external delays of unknown duration

Method 1: Rely on a Watchdog Timeout

TAOCTL0 |+= CCIE; // Enable process WDTCTL = WDTPW+WDTCNTCL; //Refresh the Watchdog timer while(CCIF == 0); // Watchdog timer could timeout if enabled Value = TAOR; // Read Result How to handle external delays of unknown duration

Method 2: Use a down-counter to prevent hanging:

timeout = 0x100; while (timeout > 0) { timeout--; if (flag == 1) //external event occurred break; } How to handle external delays of unknown duration

Method 3a: Method 3b: MEASURE the worst-case MEASURE the worst-case latency and add as delay latency and add NOPs count (slow process) (short, fast process)

// enable process // enable process TAOCTL0 |+= CCIE; TAOCTL0 |+= CCIE; delay(2000); __no_operation(); // TI NOP macro if (CCIF == 1) // flag checking __no_operation(); // TI NOP macro { __no_operation(); // TI NOP macro Value = TAOR; __no_operation(); // TI NOP macro } __no_operation(); // TI NOP macro Value = TAOR; Every Cross-Compiler has “Gotchas” Example from Code Composer Studio:

void delay(unsigned long delay_count) void delay(unsigned long delay_count) { {

while(delay_count > 1) while(delay_count > 1) { { delay_count--; delay_count--; } } __no_operation(); // TI NOP macro return; return; } }

Never put a return directly after a loop. The NOP fixes the problem The above code is “optimized” out. The Microcontroller The MSP430FR6989 Microcontroller - Overview

Registers Registers

• Control Registers (xxxCTLx)– Module level. Use to configure functions.

• Count Registers (xxxR) – Up or down counter

• Capture/Compare Registers (xxxCCR) – Work in conjunction with Counters to take action at a certain count.

• Capture/Compare Control Registers (xxxCCTLx)– Used to configure what happens when the CCR matches R (counter).

• I/O Registers (Ports) – Input/Output ports can have 1 of 4 functions. Control Registers

• Every MODULE in a Microcontroller has one or more CONTROL REGISTERS to configure the functions of the module.

• Each CONTROL REGISTER is divided into FIELDS

• Each FIELD sets one PARAMETER of the MODULE to a specific function

• The number of options for the PARAMETER determines the number of BITS in the FIELD

• Each BIT has a Power-up DEFAULT value, usually 0. Control Registers Microcontroller Registers Count Registers Microcontroller Registers Capture / Compare Registers Capture / Compare Control Registers Timer A Example:

OUT1 I/O Ports Registers Associated with each I/O Port

• Input Registers (PxIN) - Read from this one

• Output Registers (PxOUT) – Write to this one

• Direction Registers (PxDIR) – Control direction of individual bits.

• Pullup or Pulldown Resistor Enable Registers (PxREN) – Also bit by bit

• Function Select Registers (PxSEL0, PxSEL1)

• Interrupt Settings (PxIFG, PxIES, PxIE)

NOTE: x – substitute the number of the PORT • 1-10 and J, (8 bit) • A,B,C,D, and E (16 bit) I/O Ports - Summary I/O Ports – Multi-purpose Pins Secondary I/O Primary Tertiary I/O Ports Port Number and Bit Number TI Conventions

This is the number of the BIT in the PORT This is the number of the PORT P3.6 - Bit 6 of Port3 I/O Ports I/O Ports I/O Ports I/O Ports I/O Ports I/O Ports

(and possibly P3 and P4) MSP430FR6989 HARDWARE Quick Start Guide

69 I/O Ports – Putting It All Together Suppose that you wanted to configure BIT 2 of PORT 2 as a Timer B0.4 output for a PWM application

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 P2OUT X X X X X OUT X X P2DIR X X X X X 1 X X P2SEL1 X X X X X 1 X X P2SEL0 X X X X X 0 X X P2REN X X X X X X X X