PWM in microcontrollers

What is PWM? Why do we need it?

Well those of you who know about principles of communication PWM stands for pulse width modulation. Now modulation refers to change in output signal in proportion to the input signal. Now in communication systems in order to transmit data over antenna  we modulate the signal and translate it to a higher frequency. But how does PWM help in microcontrollers? Lets consider an LED which is connected to the port pin of your microcontroller. Suppose the high logic level corresponds to 3 Volts and low logic level corresponds to 0 Volt. So when you make the port pin ON the LED will get 3Volts. But suppose the LED is too bright for your application you want to change the brightness somehow. One way is to use a potentiometer to vary the voltage. But that would require an external hardware as well as manpower(you have to manually adjust the pot value). Suppose that you want to use the software and the timer hardware inbuilt in your microcontroller to change this voltage, then what would you do? The answer my friend is PWM. We give pules of varying duty cycle at the output of port pin. This in turn will give us varying average DC values.

How varying voltage is obtained using PWM?

261120131291

Thus we can vary the duty cycle and in turn vary the dc voltage available at the port pin. When we vary the duty cycle we are in turn varying the pulse width and doing pulse width modulation. This PWM can be used to vary the speed of DC motors without using a DAC(if you don’t want precise speed control you can use this.) Now about the frequency of pulses. The time delay should be small enough that there are no jerks in the motor. One other application is dimming effect in LED’s. So if you want to make LED ON in an expensive way(I’ll tell expensive why very soon) i.e by making a subtle transition from the OFF state to ON state you use PWM. Just change the duty cycle of the pulses gradually from 0 to 100%. This will give you the elegant transition. Now coming to the expensive part. Suppose you own a car and there is this interior light system which uses a proximity sensor or any other sensor to detect your presence and stars the lights. What would look more elegant to you the lights abruptly turning ON or the slow and smooth transition? Well I would like the cool effect. Now this will raise the cost of that system drastically. So these are two applications of this PWM concept.

Persistence of vision and dimming effect

Well you might know about the persistence of vision concept. If the change is taking place in less than tenth of a second then human eye is unable to distinguish the change. This concept is used in motion pictures. Now suppose your PWM frequency is 1Hz i.e 1 cycle per second. You will clearly see the LED’s turning on and OFF. So the trick is to make the LED’s toggle at a rate or frequency that gives the human eyes the illusion that LED is  continuously ON . So that is the small part that one needs to take care while using PWM for the dimming effect. But lower the frequency lower the power dissipated, so use a frequency that is just enough to make the LED’s not seem flickering.

Timer A and PWM

Well now we have a background knowledge of PWM, so let’s learn how to achieve PWM using Timer A of msp430. I had covered how to use Timer A in up mode. So we’ll learn how to obtain PWM in up mode and most importantly get the output on a port pin and know which pin it is.

Well for Timer A initializing please refer my earlier post. Now we’ll focus on OUT modes.

out_modes

Well the description given is clear enough. But still one example will aid to the understanding of the modes.

example_out_mode_in_up_mode

The Output modes are explained in terms of Timer A up mode. We’ll be using the TA0CCR0 and TA0CCR1 register for this particular experiment. Since we are using TA0CCR1 we have to see the output at the OUT1 of Timer A0. Now since this is a special output, we have to select the port special function mode. This is given in the data sheet.

port_functions

Look at the P1.2 functions. We are interested in TA0.1. So we need to make the port pin as output and then select the function 2 i.e set the P1SEL BIT2. With that we cover the PWM and its generation.

Code

/*
* main.c
* Created on    : 26-Nov-2013 1:52:12 AM
* Author        : Manpreet Singh Minhas
* Website       : https://learningmsp430.wordpress.com
*/

// Aim : To create dimming effect using PWM.

#include "msp430g2553.h";
void main(void) {
WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

P1DIR |= BIT2;               // Initialize port for Output signal
P1SEL |= BIT2;
P1SEL2 &= ~BIT2;
TA0CCR0 = 0xFF;              // Give the time delay small enough to avoid visible flickering
TA0CCR1 = 0xFF;              // Give initial duty cycle 0
TA0CCTL1 = OUTMOD_2;
TA0CTL |= TASSEL_1|ID_0|MC_1;
int i;
int j;
for(;;)
{
for(j=0;j<0xFE;j++){
for(i =0;i<3000;i++){}
--TA0CCR1;                   // Increase the duty cycle slowly
}
for(j=0;j<0xFE;j++)

{
for(i =0;i<3000;i++){}
++TA0CCR1;                   // Decrease the duty cycle slowly
}
TA0CCR1=0xFF; // Give initial count again
}
}

Alternate which involves LPM and interrupt

/*
* main.c
* Created on     : 27-Nov-2013 3:52:12 PM
* Author         : Manpreet Singh Minhas
* Website        : https://learningmsp430.wordpress.com
*/
#include <msp430g2553.h>
int i=1;
void main(void) {
WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

P1OUT = BIT0;
P1DIR |= BIT0|BIT6|BIT2;
P1SEL |= BIT2;
P1SEL2 &= ~BIT2;
TA0CCR0 = 0x7FF;
TA0CCR1 = 0x7FF;
TA0CCTL1 = OUTMOD_2;
TA0CTL |= TASSEL_2|ID_0|MC_1|TAIE;
_BIS_SR(LPM0_bits|GIE);
}

#pragma vector=TIMER0_A1_VECTOR
__interrupt void TimerA(void)
{
switch(i){
case 1:
--TA0CCR1;
if(TA0CCR1==0)
{
i=2;
}
break;
case 2:
++TA0CCR1;
if(TA0CCR1==0x7FF){i=1;TA0CCR1 = 0x7FF;}
break;
}
P1OUT ^= BIT0|BIT6;
TA0CTL &= ~TAIFG;

}

Alternate version

/*
* main.c
* Created on     : 26-Nov-2013 1:52:12 AM
* Author         : Manpreet Singh Minhas
* Website        : https://learningmsp430.wordpress.com
* Aim            : Well this is nothing but the same program written differently.
*                       In this the dimming effect will take place once when you push
*                       the button connected to P1.1.
*/
#include <msp430g2553.h>
int i;
int j;
void main(void) {
WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

P1OUT = BIT0;
P1DIR |= BIT0|BIT6|BIT2;
P1IE |= BIT1;
P1DIR &= ~BIT1;
P1REN |= BIT1;
P1OUT |= BIT1;
P1IES |= BIT1;
P1SEL |= BIT2;
P1SEL2 &= ~BIT2;
TA0CCR0 = 0xFF;
TA0CCR1 = 0xFF;
TA0CCTL1 = OUTMOD_2;
//TA0CTL |= TASSEL_2|ID_0|MC_1|TAIE;
TA0CTL |= TASSEL_2|ID_0|MC_1;
_BIS_SR(LPM0_bits|GIE);
}
#pragma vector=PORT1_VECTOR
__interrupt void Port1(void){
P1OUT ^= BIT0|BIT6;
for(j=0;j<0xFE;j++){
for(i =0;i<3000;i++){}

--TA0CCR1;
}
for(j=0;j<0xFE;j++){
for(i =0;i<3000;i++){}
++TA0CCR1;
}
TA0CCR1=0xFF;
P1IFG &= ~BIT1;
}

Effect Video

You can see the effect in this video.

Advertisements

Handling Interrupts and LPM in C language

Introduction

Well I’ve already written about handling interrupts in assembly language. Now I thought of writing about handling interrupts in C language. Since most of us are used to writing in C language, it is very important that one learns how to handle interrupts and write interrupt subroutines. Now first things first. Whatever you use be it C,C++ or assembly language, the controller understands only binary language. So when you burn the code its the hex file and nothing else that matters. Now the C language code is converted to assembly language which in turn is compiled to create the hex file. So one must know this fact to be able to use C language properly. In C language we can declare variables of various data types like char,int,etc. but when it comes to embedded C care must be taken. If your ALU size is 16 bit then the register size is 16 bit as well. So when you initialize a variable in C it is nothing but a register which is being initialized and used. Thus if you use double or any other type which is greater than the size of ALU you are bound to get error. Now to a C programmer it might seem absurd but if you know what is happening behind the scene its always easier to debug and understand what is happening. So if one knows assembly language then viewing the disassembly and debugging is easier.

Interrupts in C language

Handling interrupt is nothing but writing a code at a location whose starting address is written at the vector address of the interrupt being handled. In order to do this one must know the concept of #pragma vector. Now #pragma vector=Vector_Address is a directive just like ORG is in assembly language. What it tells the compiler is that the function that is being written next is a ISR and its starting address has to be stored at the Vector_Address. That being said, the function for ISR has a particular syntax which has to be followed.

Syntax for ISR function

__interrupt void function_name(void){}

(Note there are two underscores before the interrupt keyword). Now the function name can be anything except a keyword(which is quite obvious.). In the body of this function is what you write for handling the interrupt.

Syntax for writing a complete ISR

#pragma vector=Vector_Address

__interrupt void function_name(void)

{

// code for ISR

}

With that being covered what remains is the Vector_Address, one cannot type the vector address directly in hexadecimal value. In the msp430.h header file (rather see the device specific header file for e.g. msp430g2553.h ) there is a section for vector addresses where all vector addresses have been assigned a macro. I’ll paste the portion of header file concerning the topic at hand.

/************************************************************
* Interrupt Vectors (offset from 0xFFE0)
************************************************************/

#define VECTOR_NAME(name)       name##_ptr
#define EMIT_PRAGMA(x)          _Pragma(#x)
#define CREATE_VECTOR(name)     void (* const VECTOR_NAME(name))(void) = &name
#define PLACE_VECTOR(vector,section) EMIT_PRAGMA(DATA_SECTION(vector,section))
#define ISR_VECTOR(func,offset) CREATE_VECTOR(func); \
PLACE_VECTOR(VECTOR_NAME(func), offset)

#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define PORT1_VECTOR            “.int02”                     /* 0xFFE4 Port 1 */
#else
#define PORT1_VECTOR            (2 * 1u)                     /* 0xFFE4 Port 1 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define PORT2_VECTOR            “.int03”                     /* 0xFFE6 Port 2 */
#else
#define PORT2_VECTOR            (3 * 1u)                     /* 0xFFE6 Port 2 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define ADC10_VECTOR            “.int05”                     /* 0xFFEA ADC10 */
#else
#define ADC10_VECTOR            (5 * 1u)                     /* 0xFFEA ADC10 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define USCIAB0TX_VECTOR        “.int06”                     /* 0xFFEC USCI A0/B0 Transmit */
#else
#define USCIAB0TX_VECTOR        (6 * 1u)                     /* 0xFFEC USCI A0/B0 Transmit */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define USCIAB0RX_VECTOR        “.int07”                     /* 0xFFEE USCI A0/B0 Receive */
#else
#define USCIAB0RX_VECTOR        (7 * 1u)                     /* 0xFFEE USCI A0/B0 Receive */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define TIMER0_A1_VECTOR        “.int08”                     /* 0xFFF0 Timer0)A CC1, TA0 */
#else
#define TIMER0_A1_VECTOR        (8 * 1u)                     /* 0xFFF0 Timer0)A CC1, TA0 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define TIMER0_A0_VECTOR        “.int09”                     /* 0xFFF2 Timer0_A CC0 */
#else
#define TIMER0_A0_VECTOR        (9 * 1u)                     /* 0xFFF2 Timer0_A CC0 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define WDT_VECTOR              “.int10”                     /* 0xFFF4 Watchdog Timer */
#else
#define WDT_VECTOR              (10 * 1u)                    /* 0xFFF4 Watchdog Timer */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define COMPARATORA_VECTOR      “.int11”                     /* 0xFFF6 Comparator A */
#else
#define COMPARATORA_VECTOR      (11 * 1u)                    /* 0xFFF6 Comparator A */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define TIMER1_A1_VECTOR        “.int12”                     /* 0xFFF8 Timer1_A CC1-4, TA1 */
#else
#define TIMER1_A1_VECTOR        (12 * 1u)                    /* 0xFFF8 Timer1_A CC1-4, TA1 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define TIMER1_A0_VECTOR        “.int13”                     /* 0xFFFA Timer1_A CC0 */
#else
#define TIMER1_A0_VECTOR        (13 * 1u)                    /* 0xFFFA Timer1_A CC0 */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define NMI_VECTOR              “.int14”                     /* 0xFFFC Non-maskable */
#else
#define NMI_VECTOR              (14 * 1u)                    /* 0xFFFC Non-maskable */
#endif
#ifdef __ASM_HEADER__ /* Begin #defines for assembler */
#define RESET_VECTOR            “.reset”                     /* 0xFFFE Reset [Highest Priority] */
#else
#define RESET_VECTOR            (15 * 1u)                    /* 0xFFFE Reset [Highest Priority] */
#endif

/************************************************************
* End of Modules
************************************************************/

As you can see there is macro for each vector address of the controller. You have to use the macro, I tried using the “.int08” value but it gave error. If you find out any other way please share it with me. One can’t have enough knowledge.

So we covered the interrupt handling in C part. Now let’s see the LPM part.

LPM in C language

Well entering into LPM mode in assembly language is a piece of cake. Just set the required bits of SR and its done. When I first tried to enter into LPM in C language I wrote SR |= LPM3|GIE and I got an error. I remembered that in C we can’t use the registers directly. So I had to see an example project given in the MSP430ware. There I came across this BIS instruction equivalent function.

_BIS_SR(bits_to_be_set);

This is a function which will write BIS.W #bits_to_be_set,SR in the disassembly. So just write the bits you want to set in this function and you are ready to enter into LPM.

Example

_BIS_SR(LPM3_bits + GIE);

Program to demonstrate above concepts

/*
*  inception.c
*  Created on    : 24-Nov-2013 10:59:50 AM
*  Author        : Manpreet Singh Minhas
*  Website       : https://learningmsp430.wordpress.com
*
*  Description   : In this program we toggle the SMD LED1 after one 
*                  second delay.We use timer in up mode. I’ve enabled the TAIE so
*                  that we get an interrupt on roll over.Also we have to set GIE 
*                  bit in SR. And I’ve used LPM3 for this, since I need only the
*                  ACLK for this. (Note: The 32KHz crystal needs to be soldered 
*                  on the Launchpad.) 
*/

#include <msp430g2553.h>

void main(void)
{
WDTCTL = WDTPW|WDTHOLD;
P1DIR |= BIT0;
P1OUT |= BIT0;
TA0CCR0 = 0x8000;
TA0CTL = TASSEL_1|ID_0|MC_1|TAIE;

_BIS_SR(LPM3_bits + GIE);

}

#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
P1OUT ^= BIT0;
TA0CTL &= ~(TAIFG);
}

So I’ll be ending this post here. Hope you found it useful. Thank you for reading and as always you can ask me any doubts you have. This will help me gain new knowledge and perspective.

Interrupt concept and Low Power Modes (LPM)

Interrupts

I had  written about hardware interrupts earlier. Now I’ll write about interrupts in general. How to write program to enable interrupts and handle those. Basically there are two types of interrupts.

  • Hardware Interrupts
  • Software Interrupts

As the name suggests hardware interrupts are given on the pins of the controller. An example of this can be found in by blog post.

Software interrupts on the other hand are made by using software. And these are vectored interrupts i.e. the program counter is loaded with the contents of vector address of the interrupt that occurred. In fact all interrupts are vectored. All peripherals can generate an interrupt specific to their operation.

I’ll be taking the example of TimerA interrupt . But lets see a little theory about how to handle interrupts.

First I’ll tell you what is maskable and non-maskable interrupt.  Maskable interrupts can be disabled using software and non-maskable interrupt cannot be disabled. RST is a non-maskable interrupt. Now masking is done to avoid accidental triggering of the interrupt which can distort the program flow.

All the maskable interrupts have to be enabled. This is done by setting the GIE(General Interrupt Enable) bit of status register. So if you include the msp430.h file you can use the GIE macro defined in the header file and OR(bitwise) it with status register(SR), to enable the interrupts by setting that bit.

Now finally let’s see what happens when an interrupt occurs.

interrupt_acceptance 

So when an interrupt occurs the PC and SR contents are pushed onto the stack after the current instruction is executed. This is known as context saving. Depending on priority the interrupt with highest priority is processed first. Thus the PC is loaded with the contents of the vector address and context switching takes place.

returning_from_interrupts

The image above shows how to return from an Interrupt Sub Routine (ISR). An ISR is nothing but the code you write for handling the interrupt. The address of this ISR is stored at the vector address of the interrupt you want to handle.

One has to use “RETI” instruction to return to the main program. What RETI does is it pops the PC and SR contents back from the stack. Thus original state of program is restored.

Interrupt Vector Addresses

Now these are device specific. So one has to refer datasheet of the required microcontroller for this. Since I’ll be using msp430g2553 for this I’ll post the vector address table for the device.

vector_address_table

That is that.

Low Power Modes(LPM) of Operating modes of msp430

Now-a-days its all about reducing the power consumption. And Texas Instruments msp430 is a low power microcontroller. That means that there must be low power modes. Those are nothing but LPM. These are selected by setting or resetting the bits of SR. These modes are well explained in the user guide. I’ll post the page concerning these modes.

modes

All the SCG1,SCG0,OSCOFF,CPUOFF are bits of the status register(SR). You can use the bits defined in the header file for setting these bits. I’ll explain more about this after the program.

Now when you enter a LPM. You can come out of it when there is an interrupt. Now we know that PC and SR are pushed onto the stack. So if you want to come out of the LPM on exit from ISR you need to change the SR contents pushed onto the stack. The process is given in the user guide.

entering_exit

The bic instruction is used to clear the corresponding bits of SR that are used for controlling LPM.

In LPM basically we shut the oscillators which consume the maximum power. Thus depending on your need you can shut down the ACLK,SMCLK,MCLK and DCO.

Program

;Name             : Manpreet Singh Minhas
;Date                 : 22 Nov 2013
;Website        : https://learningmsp430.wordpress.com/
;Software        : IAR Embedded Workbench

Aim                  : To blink an LED by using TimerA interrupt and use msp430 in LPM.
#include “msp430g2553.h”     ; Include the header file
ORG 0FFFEH                                                                    ; Power On Reset Vector address
        DW MAIN                                                                    ; Here MAIN is nothing but a macro for 0C000H
                                                                                             ; memory location. You might as well write
                                                                                             ; 0C000h. We try to make the code as reader
                                                                                             ; friendly as possible, that is why MAIN macro.

                               
ORG 0C000H                                                                 ; Starting address of FLASH MEMORY in
                                                                                          ; msp430g2553 where the program will be burnt.
MAIN:        MOV.W #WDTPW|WDTHOLD,&WDTCTL  ; Stop watchdog timer
                  MOV.W #03FFH,SP                                         ; Initialize stack pointer to top of
                  MOV.B #BIT2,&P1DIR                                             ; RAM. You’ll have to use device’s
                  MOV.W #08000H,&TA0CCR0                               ; datasheet for finding this address.
                  BIC.B #BIT2,&P1OUT                                            
                  MOV.W #TASSEL_1|ID_0|MC_1|TAIE,&TA0CTL ; Initialize timer
UP:           MOV.W #LPM3|GIE,SR                                             ; Enable interrupts and enter LPM3
                 JMP UP
TMR_ISR:    XOR.B #BIT2,&P1OUT                                         ; Toggle LED
                      BIC.W #TAIFG,&TA0CTL                                      ; Clear the interrupt flag bit
                      RETI
       
ORG 0FFF0H
        DW TMR_ISR
END

The above code will make led connected P1.2 to blink after 1 sec delay. You can change the BIT2 to BIT0 or BIT6 if you don’t want to use an external LED. I’ve used TimerA in up mode. And I’m using ACLK for this part. I’ve connected a 32kHz crystal there.  So you need to connect it there by soldering. Then it’s given in the user manual that when the count goes from TA0CCR0 value to 0, the TAIFG flag is set. I’ve enabled the interrupts in SR by setting the GIE bit, also you need to enable it in the peripheral’s control register. I’ve enabled the TimerA interrupt by setting TAIE bit of TA0CTL. Now it’s vital to clear the interrupt flag of the interrupt that you are handling. In this case the TAIFG flag in TA0CTL register. So that we can acknowledge the next interrupt. Its easy to forget this so just beware. And regarding the LPM3 macro that I’ve used it just sets the corresponding bits of SR. The definition is:

#define LPM3                (SCG1+SCG0+CPUOFF)

So either you can write LPM3 or the corresponding SR bits. And instead of using mov instruction you can use bis instruction as well.

<

p>If any doubts feel free to ask. Thank you for reading this. Hope this was useful and informative.

Assembly Language Programming Inception

Introduction

Well I really wanted to write about assembly language programming, so here I am writing about it. I first came across the term assembly language when I was learning about Intel’s 8085 in my junior college. There we were introduced to this concept. When we talk with other humans we use sentences to communicate. Now these sentences are made up of words and the words from alphabets. One more thing is language, the letters are specific to the language being used. If I say ”ਤੁਹਾਡਾ ਨਾਮ ਕੀ ਹੈ” you would be able to understand this only if you know Punjabi language. So the interpretation is done by the brain in normal conversations. We come across translators who translate one language to another. Now suppose that I know only Punjabi and you know only English, then we would need a translator. This enables us to use the language we know without having to learn a new language. Now correlating this example to processors, processor only understands binary language i.e. 1’s and 0’s. We understand English(well since this is an international language I’m stating this) so we can’t write 11011110000 directly. We need some English like language for talking to the controller. This is where assembly language comes into picture. The instructions are given certain English like terms so that we humans can easily write programs and understand the written programs. Now each instruction in assembly language has an opcode which is nothing but a hex value. This hex or hexadecimal value is what the processor understands. When we were learning 8085, we used to do manual assembly. We wrote the code in assembly language, then using the opcode sheet wrote the corresponding opcodes down. Now these lines in hexadecimal numbers would be our code. This code was written into the FLASH/ROM memory. Since we manually used to write the hex values into the program memory this is called manual assembly. We talk of modern microcontrollers now. Today we get IDE’s and compilers and assemblers for all the modern microcontrollers be it msp430 or Arduino or Atmel. We write the program in English like language aka assembly language. The compiler will convert this assembly program into hexadecimal values and the assembler will burn this code in the flash memory. So the compiler generates the hex file (Now the name hex file makes sense,doesn’t it??!). This hex file is burnt into the flash memory i.e assembled into the memory  by the assembler. Now in this post I won’t be going into the details of how the program is burnt. We shall focus on how to write the code for now.

Starting with msp430 assembly language

Now that we know what assembly language is, let’s study msp430 assembly language. First thing is to learn the instructions. For that please refer the user guider  msp430x2xx for this. The section 3.4 is instruction set. The instructions are so beautifully explained there that I feel no need to explain each and everything again here. In order for anyone to write in assembly language one should know the instruction set thoroughly. Now I assume you know the instruction set from this point on!!!(If you do not understand any instruction you can comment on my blog. I’ll do the best I can to help you.) . Along with instruction set please read the addressing mode section as well. It is required as well.

CPU Registers

One of the most important resources of CPU is its registers. Most of the instructions involve them as one of the operands. Following picture shows all the registers.

registers

As we can see there are 16 registers in total. With 4 of them being special purpose registers and rest 12 general purpose registers. I’ll tell you about the PC,SP and SR for now.

PC

PC stands for program counter. This register contains the memory location of current instruction under execution. When the chip is reset,  program counter (PC) is loaded with address contained at reset vector location (0FFFEh). So when you reset the chip, [reset vector]–> PC. The reset vector address for mspx2xx is 0FFFEh. This feature is important while writing assembly language programs. I’ll talk about this when we write our first assembly language program.

SP

SP stands for stack pointer. Now this stack pointer points to top of stack. Stack is like a scratch pad where we store temporary data. We can push data onto stack. We use push and pop instructions while dealing with stack. Stack is nothing but RAM.

SR

SR stands for status register. This contains information about the result obtained after arithmetic or logical operations. Then SR bits are used to configure the low power modes in msp430. Also you can enable interrupts by setting GIE bit of SR. All in all SR is an important special register and has multiple applications.

Software

I’ll be using IAR Embedded Workbench for assembly language programs. The only reason for this is that the directives used in IAR are similar to KEIL which I learnt as a part of my curriculum. Directives are instructions used by the compiler to understand what to do with the code. You can use Code Composer Studio if you want. Note that for IAR the assembly language file is saved with .s43 extension unlike the usual .asm .

First Assembly Language program

;Name             : Manpreet Singh Minhas
;Date               : 23 Nov 2013
;Website         : https://learningmsp430.wordpress.com/
;Software        : IAR Embedded Workbench
\#include “msp430g2553.h”                                                            ;Include the header file
ORG 0FFFEh                                                                                     ;Reset vector address
DW MAIN
ORG 0C000h                                                                                     ;Flash memory starting address
MAIN:                    mov.w #WDTPW|WDTHOLD,&WDTCTL       ;Stop the watchdog timer
mov.b #BIT0|BIT6,&P1DIR                                ;Make P1.0 and P1.6 as output
mov.b #BIT0,&P1OUT                                        ;Make LED1 ON
REPEAT:             mov.w #0FFFFh,R7                                             ;Put value in counter for delay
UP:                       dec.w R7                                                               ;Decrement counter till 0
jnz UP                                                                    ;Repeat this 0FFFFh times
xor.b #BIT0|BIT6,&P1OUT                                 ;Toggle the LED1 and LED2
jmp REPEAT                                                        ;Infinite loop
END

Let’s understand what we have done in this program. I’ve written the hello world program again. This is LED blinking program in assembly language. I shall explain the basic directives now.

ORG

This directive tells the compiler that where it has to write the opcodes in memory. So ORG 0FFFFh tells the compiler that the next statement’s hex equivalent has to be stored in memory location 0FFFFh.

DW

This directive tells the compiler that we have defined a word and it is stored in memory which is given by ORG directive. So the code ORG 0FFFEh then DW 0C000h will store the word 0C000h in 0FFFEh memory location.

END

This directive tells the compiler that the code is over, whatever is written beyond this point is not relevant to the program.

Concept of LABELS

MAIN,REPEAT and UP are labels in the above program. Now these labels are nothing but the memory location of the statement where they are written. So MAIN is nothing but 0C000h, thus we could have very well written DW 0C000h instead of MAIN.

Numbers in assembly language

While writing numbers in assembly language you need to write 0 before a hexadecimal number starting with an alphabet. This is to make the compiler understand that the code is a number and not a label. I’ve developed a practice to write 0 before every number. But you can follow whatever you like. Thus FFFFh is not valid but 0FFFFh is valid. (and the 0 is zero and not the alphabet O)

Now we understand the code. The first two lines writes the starting address of the main program code at the reset vector address i.e. 0FFFEh. As I had discussed earlier that when the controller is reset the data at the reset vector memory location is loaded into the program counter, thus we write our main program starting address there. Then we write the main program in flash memory. Now for msp40g2553 flash starts at 0C000h, so I’ve written the main code there. You’ll have to change these addresses as per the data sheet of the controller you are using.

Then I’ve used simple mov instructions to initialize the watchdog timer and the ports. I’m giving this program for starting your assembly language coding. So you may want to use the timer section to give delay. I’ll write a code for that as well. Also if you have any doubts at all feel free to ask me. I’ll definitely share what I know.

Hope this post was informative. Thank you for reading this.

P.S:

Here are two more assembly language programs just for practice.

;Name             : Manpreet Singh Minhas
;Date                : 23 Nov 2013
;Website         : https://learningmsp430.wordpress.com/
;Software        : IAR Embedded Workbench
;This is a program to generate the well known Fibonacci series.
#include “msp430g2553.h”
ORG 0FFFEH                       ;RESET VECTOR
DW MAIN                        ; GOTO MAIN LABEL
ORG 0C000H
MAIN:        MOV.W #WDTPW|WDTHOLD,&WDTCTL    ; STOP THE WATCHDOG TIMER
MOV.W #03FFH,SP            ; INITIALIZE STACK POINTER
MOV.W #00H,R4            ; FIRST ELEMENT
MOV.W #01H,R5            ; SECOND ELEMENT
MOV.W #00H,R6            ; AUXILLARY POINTER
MOV.W #010D,R7            ; NUMBER OF ELEMENTS
MOV.W R4,0x200(R6)
INCD.W R6
MOV.W R5,0x200(R6)
UP:        INCD.W R6
CLRC
DADD.W R4,R5
MOV.W R5,0x200(R6)
DECD.W  R6
MOV.W 0x200(R6),R4
INCD.W R6
DEC.W R7
JNZ UP
JMP $
END

;Name             : Manpreet Singh Minhas
;Date                 : 23 Nov 2013
;Website        : https://learningmsp430.wordpress.com/
;Software        : IAR Embedded Workbench
;This program transfers data from one memory location to other.
#include “msp430g2553.h”
ORG 0FFFEH  ; This is the reset vector address.
DW MAIN
ORG 0C000H ; Starting of flash memory.
MAIN:   mov.w #WDTPW|WDTHOLD,WDTCTL ; stop the watchdog timer
mov.w #03ffh,SP            ; initialize the stack pointer to top of ram
mov.w #0200h,r4            ; initialize the destination reference pointer
mov.w #0e000h,r5           ; initialize the source pointer
mov.b #010d,r6
up:     mov.w @r5+,0(r4)
incd.w r4
dec.w r6
jnz up
jmp $
ORG 0E000H
DB “MANPREETSINGHMINHAS”
END

For both these you can use the IAR emulator. I’ll cover how to use that later.

16×2 LCD interfacing in 4 bit mode

Hello there!! After trying many times I finally managed to interface LCD in 4 bit mode. I’m writing this post as soon as I got the output and understood what was going wrong. First things first, if you are reading this article directly without knowing about 8 bit mode interfacing then you should read my blog post on 8 bit LCD interfacing. Assuming that you know what 8 bit mode is, let’s begin with the 4 bit mode interfacing.

Need for 4 bit mode

Well for interfacing anything with any processor we need system-bus (data-bus, address-bus and control-bus). In our case for 8 bit mode the 8 data pins (D0-D7) are the data and address bus while the 3 control pins(RS, R/W and E) are the control bus. Thus using these we can control the peripheral that we are interfacing. We are greedy so we want to interface as many peripherals as possible with the same microcontroller. This requires either large number of ports or we need to be smart and utilize what we have to the fullest. Thus first thing is we try to do is reduce the number of pins required for controlling the peripheral. Here comes the need for 4 bit mode. Thus we reduce the port pins required from 11 to 7. It might not seem much, but for a small microcontroller like msp430g2553 with limited port pins this is really a big amount. Now coming to the other method. Maybe we can use demultiplexing , in this way we can use ‘n’ lines to share the system bus with ‘2^n’ devices. I got one tip that we can use SIPO shift register for sending data. Now this will require only 5 port pins. Three control pins and two for serial data and clock.

4 bit mode working

In 4 bit mode we send the data nibble by nibble, first upper nibble and then lower nibble. For those of you who don’t know what a nibble is: a nibble is a group of four bits, so the lower four bits (D0-D3) of a byte form the lower nibble while the upper four bits (D4-D7) of a byte form the higher nibble. This enables us to send 8 bit data be it the ASCII code or the command code by using 4 pins instead of 8. The connections remain identical. The only change is that the lower nibble pins of LCD are unused.

Initializing the LCD in 4 bit mode

This is perhaps the most tricky part of this interfacing. When LCD is powered ON it is by default in 8 bit mode.

reset

This is the command code format for function set operation. Using this we form the command code for 4 bit mode operation.

upper part

mode_set

The function set bits are explained in the following section.

functions_mode_set_bits

We need 4 bit mode so make DL ‘0’. Thus the upper nibble of the command code is 0010b which is 0x02. If we need 2 lines we set N and the normal font so F is 0 thus lower nibble comes out to be 1000b i.e 0x08. Thus total command code is 0x28.

Before sending this 0x28 we need to perform a specific initialization.

initialization

So we send 0x33 as the command code. This will do the initial 8 bit mode starting of LCD. Now after this we need to send 0x32.(Note : We’re using the nibble method so what will go for 0x33 is 3 followed by 3 and for 0x32 is 3 followed by 2.) I was not able to do the initialization. But then I suddenly remembered about a workshop that I had attended on msp430. The professor had taught LCD interfacing in 4 bit mode but due to shortage of time could not go into the details. He had just told about the nibble sending part and that we need to send command codes to initialize. So I opened his file and saw this 0x33 and 0x32 and tried sending it. And to my surprise I got my name on the LCD. So this credit goes to him. His name is Gurjeet Singh Gill.

Now coming back to the topic at hand. So now we have initialized the LCD in 4 bit mode. All we need to learn is how to send the value nibble by nibble in c and without affecting other port pins except the ones that we use for sending data. (In assembly its just the rotate instruction.)

Nibble sending logic

P1OUT = (P1OUT & 0xF0)|((data>>4) & 0x0F); // send higher nibble

In this we make use of masking concept and logical shift operation in c. Now in my program I’m using P1.0 – P1.3 pins as the data pins. Thus I’ll explain the logic accordingly. Here data is the parameter that is passed in the function. I shift the data right by four bits, thus bringing the higher nibble in the lower nibble location. Mask the upper part. Then I make the P1OUT lower nibble 0 so that ‘or’ing with nibble data will give nibble data on P1OUT pin. (x and 0 is 0 ; x and 1 is x; x or 1 is 1 and x or 0 is x—-> (x&0)|(data_bit)= 0|data_bit= data_bit. Thus we get data bit on the port pin without affecting P1.4-P1.7) This takes care of upper nibble.

P1OUT = (P1OUT & 0xF0)|(data & 0x0F); // send lower nibble

Now lower nibble involves the same operations except the shifting operation.

Code

Header file :

// Author : Manpreet Singh Minhas
// This file is for 4 bit mode LCD interfacing with msp430g2553 chip
// 16x2 LCD is used
#include &lt;msp430g2553.h&gt;
#define DR P1OUT = P1OUT | BIT4 // define RS high
#define CWR P1OUT = P1OUT &amp; (~BIT4) // define RS low
#define READ P1OUT = P1OUT | BIT5 
// define Read signal R/W = 1 for reading
#define WRITE P1OUT = P1OUT &amp; (~BIT5) 
// define Write signal R/W = 0 for writing
#define ENABLE_HIGH P1OUT = P1OUT | BIT6 
// define Enable high signal
#define ENABLE_LOW P1OUT = P1OUT &amp; (~BIT6) 
// define Enable Low signal
unsigned int i;
unsigned int j;
void delay(unsigned int k)
{
for(j=0;j&lt;=k;j++)
{
for(i=0;i&lt;100;i++);

}

}
void data_write(void)
{
ENABLE_HIGH;
delay(2);
ENABLE_LOW;
}

void data_read(void)
{
ENABLE_LOW;
delay(2);
ENABLE_HIGH;
}

void check_busy(void)
{
P1DIR &amp;= ~(BIT3); // make P1.3 as input
while((P1IN&amp;BIT3)==1)
{
data_read();
}
P1DIR |= BIT3; // make P1.3 as output
}

void send_command(unsigned char cmd)
{

check_busy();
WRITE;
CWR;
P1OUT = (P1OUT &amp; 0xF0)|((cmd&gt;&gt;4) &amp; 0x0F); // send higher nibble
data_write(); // give enable trigger
P1OUT = (P1OUT &amp; 0xF0)|(cmd &amp; 0x0F); // send lower nibble
data_write(); // give enable trigger

}

void send_data(unsigned char data)
{
check_busy();
WRITE;
DR;
P1OUT = (P1OUT &amp; 0xF0)|((data&gt;&gt;4) &amp; 0x0F); // send higher nibble
data_write(); // give enable trigger
P1OUT = (P1OUT &amp; 0xF0)|(data &amp; 0x0F); // send lower nibble
data_write(); // give enable trigger
}

void send_string(char *s)
{
while(*s)
{
send_data(*s);
s++;
}
}

void lcd_init(void)
{
P1DIR |= 0xFF;
P1OUT &amp;= 0x00;
send_command(0x33);
send_command(0x32);
send_command(0x28); // 4 bit mode
send_command(0x0E); // clear the screen
send_command(0x01); // display on cursor on
send_command(0x06); // increment cursor
send_command(0x80); // row 1 column 1
}

Main Code:

/* LCD_own.c
* Created on: 12-Nov-2013
* Author: Manpreet
* In this program we interface the lcd in 4 bit mode. We send strings and display it on the screen.
*/
#include "lcd.h"

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // stop watchdog timer
lcd_init();
send_string("Manpreet Singh");
send_command(0xC0);
send_string("Minhas");
while(1){}
}

P1.0 – D4            Pin11

P1.1 – D5             Pin12

P1.2 – D6             Pin13

P1.3 – D7             Pin14

P1.4 – RS             Pin4

P1.5 – R/W         Pin5

P1.6 – E                 Pin6