Search Microcontrollers

Wednesday, August 29, 2012

DYI PCB

Today I made my first homemade Printed Circuit Board (PCB).

In the past I was lucky enough I could get access to some lab to produce them for me and today, does it still make sense to etch your PCB at home?

You probably get a much better result by using one of the many available online services that can produce professional grade PCBs for a reasonable price.... but that might eventually work if you know what you are doing... which, sadly, it's not my case.

By that I mean that any circuit I design and then test on a breadboard, has good chances it will not work once transferred on a PCB.
Why?
The answer is really simple : lack of experience which you can build up with the most classical process called "trial and error".

Now, if for every try and probable error I need to wait weeks for the pcb to be delivered to me I would consider that sub-optimal, it would not provide me a really steep learning curve.

I remember some years ago I saw a great BC comic strip :
BC (or was it Thor?) is pushing a square stone with a hole in the middle and says something like : "I had a wonderful Idea! This thing is called wheel and it will change the world as we know it".
In the next panel he comes back with  a triangular stone and goes : "I made a great improvement to my invention" and the other guy : "Uh? Where's the improvement?".
The answer is "I eliminated one of the four bumps".

So, to set the stage, I am pretty much there now : bumps optimization.
Not much, I agree, but it's always a good start to know were you stand.

I usually build circuit prototypes with breadboards, but it 's becoming more and more cumbersone since most of the components are now available in SMD packages.
I know there are adapters, but in the end I figured it's easier to be able to etch a quick PCB and use it instead... would have been fun anyway, so I thought.


Before I dig in further more : it actually worked, better than I expected (maybe my expectations were pretty low).
The board in the picture is 4 x 2.5 cm, pretty small... those pads on the bottom are for smd 805 components.
This should give you an idea of the size of the traces.

This one is just a test, my very first test, so I am pretty sure every step, from design onwards can be improved and yes, I could have started with something "simpler", maybe with bigger traces etc, but the purpose of this one was really to see where's the limit.

I still need to apply solder mask, so the process is not over yet.

How this was achieved :

I designed the pcb using Eagle Cad.
Nice tool, I chose this one mainly because you can type commands instead of clicking icons, I know it might sound silly to you, but it helps me a lot since my eyes make it difficult for me to use icons.
I created my own library with the two packages I needed, in that way I was able to design the pads with the shape I thought best to ease the soldering afterwards.
Soldering those tiny chips (and mainly the LDO on the right) will be difficult, hopefully I will have a good solder mask soon, without that I would not even try.
I designed the pads a bit longer than the suggested measure, I probably should have done more than that.
The idea is that it should be used to drag the solder away after applying it.
I suppose I will need to use the solder wick to remove shorts between the pins, but that's still to be discovered (wow, isn't that fun? So many things to try out!)
  
The circuit itself is really simple, it's a RS485 driver with a 3.3V LDO voltage regulator, nothing fancy, it should replace the one I am using on a breadboard.
It is also a part of a bigger circuit I am designing to host a MSP430G2553 (TSSOP). 



Once the PCB design was done I "panelized" 6 circuits (they are tiny, makes sense to run a batch of 6 at least) and printed some tests on plain paper, checking also that the solder mask overlapped nicely to the pads once printed.
Another check I did was to place the two chips on the paper and verify the contacts were sitting nicely on the pads (I designed the footprints in Eagle, had to test it).

To transfer the design to the copper I found two options you are probably familiar with :
- Photo resit and UV  light
- Toner Transfer

I have the equipment for the UV thing, a built myself a nice bromograph (I am quite proud of it :) Simple but apparently reliable), with 4 8W UVA lamps with ballasts and nice reflecting white inside. 
Yeah, told you I was proud of it, no? :)
But finally opted for the toner transfer as I found in the web people very happy with the results.
I will still use the bromograph for the solder mask.


   
The panel is laser printed (mirrored) on the toner transfer sheet (I had A5 sheets, managed to cut one in half for this test, to reduce the wasted material).
After cleaning the copper surface (dish soap first to remove finger prints etc, then a metallic wool sponge -gently- and water) I placed the pcb on a tempered glass (you can get them really cheap at Ikea, just look for shelves) with a bit of bi-adhesive.


Using a glass there helps a lot, it's a smooth solid surface, thermally suitable for this job and helps a lot also in aligning the toner transfer sheet with the board, I left on purpose some black borders around, to easily overlap the two parts.
Once the board was placed on the glass, I cleaned it with isopropilic alcohol.


You cannot see it properly in the picture, because of the flash, but placing the glass against a light source it is possible to use the black border to center the sheet and than secure it with some tape.


I initially overlapped a piece of fabric as I feared the yellow paper might stick to the iron, then I realized it is made specifically to be ironed... so it does not stick, nor it burns.
Thanks to the tape and the bi-adhesive the paper did not move even if I feared it might once it was really hot.
I then realized I might have overheated it, especially in the center.
Since everything is stuck to the glass, it's quite easy to handle, I used some cold water to cool down everything before peeling off the toner transfer sheet... which proved to be quite an easy task.


As you can see, in the center there is still a bit of toner, it probably happened because I overheated that area or because of the bi-adhesive tape on the back which absorbed part of the pressure I applied.
Checking the copper side revealed further signs of overheat, the copper appeared "reddish", darker than in the other area and  the toner was not covering completely the copper in some points.


I initially thought that the two boards in the middle would have been useless, but turns out I was wrong.


 I then etched using the classic FeCl solution, if you never used it, that thing is really messy, better use protection gloves and to keep it away from any metallic thing you are not planning to corrode.
To speed up the process, while gently rocking the container with one hand (while not holding he camera), I heated the solution with a hot air gun.
Combined with the fact that I tried to optimize the design to reduce the amount of copper that needed to be removed, the process was quite fast, probably around 5 minutes.


 The board is then rinsed with a lot of cold water.
A this point you can remove the remaining toner, but, if you are not planning to go ahead with the next steps, it's probably better to leave it there at it somehow protects the copper from further oxidation.



In my case I need to first check the result and then proceed with the solder mask part.
Since I am not sure at all of the outcome of the solder mask process, I decided to split the board in two to have two chances to run through the process, I then cleaned one.


I used a regular cutter, which did the job... but was probably not the best solution.
Using an aluminium guide to ensure the cut was traight helped a lot, specially since it was secured with two clamps.
These things make the job safer and easier, they come in extremely handy and can be easily found in hardware stores.


cleaning the toner away revealed that even the middle board, which I thought was useless due to the overheating, was pretty good.
In fact the first picture on top is the middle board, probably the worse in the batch.
Ok, I know, it could have been better, but you should realize those traces are really tiny, I could not find any short or broken trace after inspecting the pcbs with a magnifier.
The worse point is the one shown in the circle below.
It's the overheated part of the middle board, not perfect, but not too bad either in the end.
Check the pad on the right (red rectangle), it's 0.4 by 0.9 millimeters, that should give you an idea of the sizes in the picture.


Overall, for being my first attempt I am quite satisfied with the result, now I need to work out the solder mask part and then... the hand soldering.
   


Thursday, August 23, 2012

MSP430G2 - Pull up / down resistors

When using GPIO lines as inputs, you normally need to use pullup or pulldown resistors.
Most of the modern mcus now have them built in, selectable via software.

To understand why you need them I propose a simple and funny experiment with the MSP430 Launchpad.

Let's setup an extremely simple application, something that flashes the leds if we press the S2 button on the lanchpad.

The key thing here is that we are using a gpio input (p1.3) to detect whether the s2 button is pressed or not.
The flashing leds will just provide feedback on the fact that, yes, the button was pressed.

In order to show why we need pull resistors we will not configure one at the beginning

/*
 * main.c
 */

#include <msp430g2553.h>

void clockConfig()
{
 BCSCTL1 = CALBC1_16MHZ; // Set DCO
 DCOCTL = CALDCO_16MHZ;
 BCSCTL2= DIVS_2 + DIVM_0; // divider=4 for SMCLK and 1 for MCLK
}

void pinConfig()
{
  P1DIR |= BIT0  + BIT6; // leds = output
  P1DIR &= ~BIT3; // s2 = input
  P1OUT &= ~BIT0; // turn leds off
  P1OUT &= ~BIT6;
}

void main(void)
{
 WDTCTL = WDTPW + WDTHOLD;
 clockConfig();
 pinConfig();
 while (1)
 {
if ((P1IN & BIT3)>0) // if button bit is high
{ // flash leds
  P1OUT |= BIT0;
   P1OUT &= ~BIT6;
__delay_cycles(10000000);
  P1OUT &= ~BIT0;
   P1OUT |= BIT6;
         __delay_cycles(10000000);
}
 }


This experiment could eventually lead to unpredictable results (no worries, nothing will blow up -but hey, if it does don't call it on me! :P -), but hopefully we will be able to demonstrate the issue.

Now, the "magic" part :

Compile, load and run this simple software, let it run for a few seconds and watch the leds.
Nothing interesting happening there, right?
Ok, now without pressing it, touch with a finger the side of the button S2, you should survive this action.... unless a tiny and extremely poisonous scorpion was hiding behind it.

What happens?
The first thing is that you did not die, which is already a success for the experiment, but the most important thing is that eventually (depending on electrical charges on your body) you might have been able to trigger the state of pin p1.3.

While it looks pretty cool, we normally don't want that.
An input line left alone that way would eventually capture electrical charges from the surrounding environment, acting like an antenna.
The potential of such antenna would then be "floating" and it's difference to ground can be enough sometimes to reach a "1" logic state.

If we permanently connect a resistor (could be something like a 10K ohm) to ground we force -with a tiny current- the potential to level with the ground.
The amount of current needed to ensure this is in general very small, unless you have some extreme cases like you are designing circuitry for a nuclear weapon and such... but that's ok, I actually had to declare I will not use Code Composer Studio to create mass destruction weapons already.

Note : Isn't it funny' there is a country in the World were apparently you can buy an assault rifle with few question asked, but it gets tricky to download a C compiler just in case you could use it to produce weapons, which, admittedly, is the main usage of a C compiler :) 
Peace and love, stop using your compilers to kill people around the world (and smartly declaring it in online export forms at the same time)! 
Ah, I will ask you to sign some kind of declaration where you promise no to use my idea of hiding tiny scorpions behind push buttons.

Back to our resistors, the MSP430G2 enables them with the register P1REN, so let's just add the line

P1REN |= BIT3; 

in the pinconfig procedure.
Re-build, reload, ad re-execute and try again to touch the side of the button.
Yeah, I know, it was much more fun before, right?
 

Thursday, August 16, 2012

C2000 Piccolo - The clock system

The TMS320F2802x has a quite flexible clock system, still providing easy ways of interacting with it.

Texas Instruments provides an extensive guide (TMS320F2802x/TMS320F2802xx Piccolo System Control and Interrupts), which covers several topics including the clock system.

There are four main options when it comes to selecting a clock source :
- Two on chip (zero pin) digital oscillators (INTOSC1 and INTOSC2)
- An on chip clock module that uses an external crystal
- An external clock signal

The two internal (10MHz) clocks can be used at the same time to feed the signal to different subsystems (CPU code, Watchdog, Timer 2).

A PLL (Phase Locked Loop) Module receives the clock signals and provides a set of multipliers and dividers to adjust the frequency as needed.
This defines the core Clock SYSCLKOUT, a slower subClock (LSPCLK Low Speed Peripheral Clock) is then obtained by pre-scaling the core clock.

Peripheral modules source SYSCLOCKOUT (eCAP,ePWM...) or  LSPCLK (SCI, SPI...) and dedicated registers are used to enable or disable their clock input.

The Low Speed Peripheral Clock has a dedicated Prescaler Register (LOSPCP) that allows a (core clock) frequency division by a range from 1 to 14.

The clock registers allow to define which peripherals are clocked or not.
Specific functions are provided (clk.h) to enable or disable the clock signal generation for the peripherals, such as CLK_enableAdcClock(myClk); or  CLK_enableSciaClock(myClk);

From a  coding point of view, when dealing with the clock system, we need at least to get a handle for the Clock and PLL modules :


 CLK_Handle myClk;
 PLL_Handle myPll;
 myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
 myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));

As previously explained there are two internal and two external oscillators / clock signals.
To configure them two functions are provided :


void CLK_setOscSrc(CLK_Handle clkHandle,const CLK_OscSrc_e src);
//    src = CLK_OscSrc_Internal or  CLK_OscSrc_External                   

void CLK_setOsc2Src(CLK_Handle clkHandle,const CLK_Osc2Src_e src);
//    src = CLK_Osc2Src_Internal or  CLK_Osc2Src_External



As the internal oscillators have a fixed frequency of 10MHz, we need to use the PLL to multiply/divide it to match the value we need.


    PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);

// multiplier is any number between 1 and 12 (on the tms320f28027, 
// other mcus may support different frequencies)
// divider is  [ 1,2,4 ]
// the actual frequency is  F = 10MHz * PLL_Multiplier / PLL_Divide

With this we are all done setting the CPU frequency, can we test it?
The answer is absolutely yes, and this is made easy by the fact that we can "expose" the clock signal externally through a pin of the processor, we just need to set the right value of the multiplex for the pin GPIO_18 and then eventually set a divider (prescaler) for this signal.

  GPIO_Handle myGpio;
  myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));

  GPIO_setMode(myGpio, GPIO_Number_18, GPIO_18_Mode_XCLKOUT);
  CLK_setClkOutPreScaler(myClk, CLK_ClkOutPreScaler_SysClkOut_by_4);  
// prescaler values (1,2,4,Off)
// CLK_ClkOutPreScaler_Off  turns off the clock signal on the pin

Now we need to hook up an oscilloscope probe to the GPIO_18 (J1 pin 7 on the launchpad)



The complete source code for this test is the following :

#include "DSP28x_Project.h"     // DSP28x Headerfile

#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/wdog.h"

void main()
{
 WDOG_Handle myWDog;
 myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));
 WDOG_disable(myWDog);

 CLK_Handle myClk;
 PLL_Handle myPll;
 myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
 myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));

//Select the internal oscillator 1 as the clock source
  CLK_setOscSrc(myClk, CLK_OscSrc_Internal);

  PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2); //60MHz

  GPIO_Handle myGpio;
  myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));

  GPIO_setMode(myGpio, GPIO_Number_18, GPIO_18_Mode_XCLKOUT);
  CLK_setClkOutPreScaler(myClk, CLK_ClkOutPreScaler_SysClkOut_by_1);

  while(1)
  {
    // I was blinking a led here
  }

}

The result is visible in the picture below, you will notice the waveform is a bit weird, but I did not put the required attention on how I hooked the probe for such an high frequency signal, that might explain it.
I was mainly interested in checking the frequency which you can read in the bottom right corner of the screen.
Yup, that's a 60MHz! (note : remember to disable the 20MHz band limit on the channel :P) 



If we play a bit with the prescaler :

  CLK_setClkOutPreScaler(myClk, CLK_ClkOutPreScaler_SysClkOut_by_4); 
// 60 /4 = 15MHz



And again, besides the waveform which is probably a bit different from the real one, it's a confirmed 15MHz.

There are plenty of other interesting details to discuss about the clock system, however they tend to be specific to the peripherals that use the clock so, I believe, they are better discussed in that context.

Sunday, August 12, 2012

C2000 Piccolo - blinking an LED

Ok, this is my first program with the C2000 Launchpad, so I would start with something really basic, the MCU "Hello World" : Blinking an LED.

First of all, let's add the basic things we will probably always need :
We need the basic header file, we probably need to set the clock (clk + pll), stop the watchdog and access the gpio to blink the LED.

I recon we could start with something like this :


#include "DSP28x_Project.h"     // DSP28x Headerfile

#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/wdog.h"



void main()
{

 WDOG_Handle myWDog;
 myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj)); 
 WDOG_disable(myWDog);
}

I know, this does not do much, right?
Let's see how we can add some more interesting stuff

  CLK_Handle myClk;
  PLL_Handle myPll;
  myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
  myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));


 //Select the internal oscillator 1 as the clock source
   CLK_setOscSrc(myClk, CLK_OscSrc_Internal);

The clocking user guide states that there are two internal oscillators, both working with a base frequency of 10MHz, there are obviously also external options which we will not consider for this experiment.
To select the second internal oscillator we should use "CLK_Osc2Src_Internal"
Based on the oscillator frequency (10MHz if internal) the PLL (each internal oscillator has a pll) can be configured to set a multiplier and divider.

 PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);
// the demo program states : 
// Setup the PLL for x10 /2 which will yield 50Mhz = 10Mhz * 10 / 2
// it looks more a 10MHz * 12 / 2 = 60 to me, which is the max speed of this device (and the multiplier_12 is the maximum value found in the enum declared in the header file)

the multiplier has a value from 1 to 12 and the divider can be 1,2,4.

The blue leds installed on the board (C2000 launchpad) are connected to gpio 0,1,2 and 3.
We will just use gpio_0, so let's configure it as output gpio pin.

 GPIO_Handle myGpio;

 myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));

 GPIO_setMode(myGpio, GPIO_Number_0, GPIO_0_Mode_GeneralPurpose);
 GPIO_setDirection(myGpio, GPIO_Number_0, GPIO_Direction_Output);

Actually, I found out that to turn the onboard LEDs we need to drive the signal low, and to turn them off the signal must be high.
For this reason, by default, all 4 leds are on (or at least this happened to me), so I specifically switched all them off before the loop for the blink.


    GPIO_setMode(myGpio, GPIO_Number_0, GPIO_0_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_0, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_1, GPIO_1_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_1, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_2, GPIO_2_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_2, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_3, GPIO_3_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_3, GPIO_Direction_Output);

  GPIO_setHigh(myGpio, GPIO_Number_0);
  GPIO_setHigh(myGpio, GPIO_Number_1);
  GPIO_setHigh(myGpio, GPIO_Number_2);
  GPIO_setHigh(myGpio, GPIO_Number_3);



Now, let's add a loop that turns the led on and off and le'ts cross our fingers :)

while(1)
{      
  GPIO_setLow(myGpio, GPIO_Number_0);
  DELAY_US(1000000);
  GPIO_setHgh(myGpio, GPIO_Number_0);
  DELAY_US(1000000);
}

Build, debug...
Does it work?
Not as expected for me, but I found out why.
What happened is that the delay was completely skipped.
By control-clicking it I found that function is actually defined in the DSP2802x_Examples.h file.
Something came back to my mind, while reading about the C2000 workshop I read something about memory segments and stuff that must be placed in the correct place... and I told to myself : "do I really need to care about that thing?".
Turns out that, yes, I have to.
To be honest, I didn't really get how the thing works yet (still reading), but the solution is to copy the functions from one area to another (or whatever is accomplished with the following lines).


#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
#endif

That, does the trick, but really, I will not attempt to explain it right now, I need to better understand it before.
Finally, this is my led blink source code :


/*
 * main.c
 */

#include "DSP28x_Project.h"     // DSP28x Headerfile

#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/wdog.h"


#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
#endif


void main()
{
 WDOG_Handle myWDog;
 myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));
 WDOG_disable(myWDog);

 CLK_Handle myClk;
 PLL_Handle myPll;
 myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
 myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));

  CLK_setOscSrc(myClk, CLK_OscSrc_Internal);

  PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);

  GPIO_Handle myGpio;
  myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));

  GPIO_setMode(myGpio, GPIO_Number_0, GPIO_0_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_0, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_1, GPIO_1_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_1, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_2, GPIO_2_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_2, GPIO_Direction_Output);
  GPIO_setMode(myGpio, GPIO_Number_3, GPIO_3_Mode_GeneralPurpose);
  GPIO_setDirection(myGpio, GPIO_Number_3, GPIO_Direction_Output);

  GPIO_setHigh(myGpio, GPIO_Number_0);
  GPIO_setHigh(myGpio, GPIO_Number_1);
  GPIO_setHigh(myGpio, GPIO_Number_2);
  GPIO_setHigh(myGpio, GPIO_Number_3);

  while(1)
  {
    GPIO_setLow(myGpio, GPIO_Number_0);
    DELAY_US(1000000);
    GPIO_setHigh(myGpio, GPIO_Number_0);
    DELAY_US(1000000);
  }

}

... and it blinks.

I also found a few issues with the linker not finding whatever it needed, I temporarily solved by cloning the demo project, removing the source and adding my new source.
Yup, I still have plenty of things to learn, that's he beauty of it, right?

[update : thanks to Trey@TI I found out what needs to be specified in the project properties when creating a new project from scratch (reporting it here just in case you encounter the same issue) :

1) in project -> properties -> CCS Build -> C2000 Compiler -> Include Options -> add dir : "C:\ti\controlSUITE\development_kits\C2000_LaunchPad"
2) in project -> properties -> CCS Build -> C2000 Linker -> File Search Path -> Include library : "C:\ti\controlSUITE\development_kits\C2000_LaunchPad\f2802x_common\lib\driverlib.lib"



]

C2000 Piccolo - Coding - Basics

The C2xxx, like all the common MCUs has several modules performing different functions, such as ADC, PWM, Timers, UART etc.
On the coding side, to partially "hide" the complexity behind this a set of header files are provided, specific to each mcu family in the range.

Those header files contain the methods needed to operate the module they are referred to, and each one of them provides a "handle".

As an example the Clock module is supported by the clk.h header file :

#include "f2802x_common/include/clk.h"

this header file defines a CLK_Handle pointer to a CLK_Obj structure, which we will use to interact with the clock.

CLK_Handle myClk;
myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));

CLK_BASE_ADDR is the base address for the clock registers and its value is also defined in clk.h.
What this init does is to simply point the structure to the actual address in memory of the registers needed to operate the clock.
In fact, if ye check the clk.h file , we discover that there is this define :

#define  CLK_BASE_ADDR                   (0x00007010)

Checking the tms320f28027 datasheet, we discover that at that address there are the System Control Registers


There is a specific technical document called "TMS320F2802x/TMS320F2802xx Piccolo System
Control and Interrupts" which describes that particular functionality (analogue guides are available for ADC, PWM etc).


if we check in clk.h how the Clk_Obj structure is defined, we obviously find a matching definition (although in some case not the full register range may be mapped).


typedef struct _CLK_Obj_
{
    volatile uint16_t   XCLK;         //!< XCLKOUT/XCLKIN Control
    volatile uint16_t   rsvd_1;       //!< Reserved
    volatile uint16_t   CLKCTL;       //!< Clock Control Register
    volatile uint16_t   rsvd_2[8];    //!< Reserved
    volatile uint16_t   LOSPCP;       //!< Low-Speed Peripheral Clock Pre-Scaler Register
    volatile uint16_t   PCLKCR0;      //!< Peripheral Clock Control Register 0
    volatile uint16_t   PCLKCR1;      //!< Peripheral Clock Control Register 1
    volatile uint16_t   rsvd_3[2];    //!< Reserved
    volatile uint16_t   PCLKCR3;      //!< Peripheral Clock Control Register 3
} CLK_Obj;


Now we can control the clock subsystem using the handle :

CLK_enableAdcClock(myClk);

Notice that  CLK_enableAdcClock is a normal function, not a method of an object, it will take the handle as parameter, allowing cross compilation with other devices of the family, supported by different header files and therefore with different addresses.

At this point we can also configure the peripheral, in the next case we set the source oscillator to be the internal one.

CLK_setOscSrc(myClk, CLK_OscSrc_Internal);

When performing these tasks, we typically chose an option from a list, and there is a easy way to get the full list in order to see the other options.
If you press ctrl and you hover your mouse over an expression or a constant, in some cases the text gets underlined.
If this happens CCS (Eclipse) can take you to the declaration of such expression, simply click with the mouse while keeping the ctrl key down.

Ctrl-clicking over CLK_OscSrc_Internal we are taken to the clk.h file where the enumeration  CLK_OscSrc_e is defined with its two options and their bits settings (which we don't need to care of). 

typedef enum
{
    CLK_OscSrc_Internal=(0 << 0),  //!< Denotes an internal oscillator source
    CLK_OscSrc_External=(1 << 0)   //!< Denotes an external oscillator source
} CLK_OscSrc_e; 

This is a rather simple way of discovering the options offered by the different modules.
While in order to grasp the concepts related to a specific peripheral it is always advisable to check the pdf guide, this may give a quicker way of discovering the various options supported.






Friday, August 10, 2012

MSP430G2 - Timers and PWM

We previously used a timer (TIMER A) to execute a procedure at regular intervals.
This is definitely a useful way of using the timers, but there is much more we can do with them.

Another interesting application is the generation of PWM (Pulse Width Modulation) signals.
I am not planning to explain here what PWM is, and I will assume you are familiar with the basic concepts (Frequency and Duty Cycle), if not you should be able to find everything here :
http://en.wikipedia.org/wiki/Pulse-width_modulation.

These signals have  a number of different practical applications, they can be used to control precisely the speed of electric motors, to drive power transistors in switching power supplies, they can be used to dim a led light etc.

A PWM signal is characterized by two parameters : Frequency and Duty Cycle.
The MSP430G2 time handles both of them.

The first thing we have to define is the period T (which also defines the frequency F=1/T).

Let's image to run the Timer A  at his full speed (16MHz) having the MCLK sourced from DCO with divider = 1 and SMCLK, also selected as clock source for Timer A, with the minimum division : 1
Let's also assume that Timer A is configured to run in "up mode", basically it will count from 0 to the values set in TACCR0.
Each count will take 1 / 16.000.000 seconds (1 cpu clock), so, if we make the timer to count 8 times, the period will be :

T =   1 / 16.000.000 * 8 = 1 / 2.000.000 s
and obviously the frequency F = 1 / T = 2MHz

TACCR0 has to be set to 7 for this (the count starts from 0)

Defining the Duty Cycle means we are defining the period for which the signal should be on (Ton) in each cycle.

Since the MSP430 has several capture/compare devices per each timer, we can use a second one (CC1) to define Ton.
The duty cycle is defined as Ton/T, but we also know that the periods are directly proportional to the number of timer counts, so, assuming we want to obtain a 25% Duty cycle

 0.25 = Ton / T

We know T = 8 -> Ton = 8 * 0.25 = 2


TACCR0 = 7;  // PWM Period (CC0)
TACCR1 = 1;  // PWM Ton   (CC1)          


In this picture, on the Y axis we have the Timer A counter value, on the X Axis each block is is a counter clock (in our case 1 / 16.000.000 s).
Since the timer is configured in "up mode", it will start from 0 and increment by one until it reaches TACCR0 (7) then it will drop again to 0.
Another action will be triggered when TACCR1 is reached.
How the CC0 and CC1 triggers can be combined is defined by choosing the "output mode" of the time as visible in fig 12-12 (from TI's MSP430 User guide).
The waveform we want to obtain is the red/green one, which matches with Mode 7.



This is chosen setting the right value in the TACCTL1 register

TACCTL1 = OUTMOD_7;

Obviously we could just set up an interrupt, check the TAR counter and drive accordingly an output pin, but that would work only for low frequencies as the cpu cycles needed to compare the values and drive the pin would affect our waveform, plus we would be using the CPU almost exclusively for this purpose.

Luckily the timer is autonomous for this and can automatically drive an output pin, using the criteria defined by the mode selected (reset/set in our case).
This means we do not need an interrupt nor we need to do anything special with the CPU which could be "sleeping" or performing other tasks.

Checking the device specific Datasheet (msp430g2553 in my case) we find that pin P1.2 supports the function TA0.1 (timer A output) with mode 1 (PSEL = 1 , PSEL2 = 0, DIR = output -1-).


The bit related to pin 1.2 is the 3rd in the registers :

7654x3210 
0000x0100
and 0100b = 0x4 in hex  (also 2^pinNbr)


P1DIR |= 0x04;     // P1.2  set as output
P1SEL |= 0x04;     // P1.2  TA0.1 option

So globally the test program is :

#include <msp430g2553.h>

void clockConfig()
{
 // configure the CPU clock (MCLK)
 // to run from DCO @ 16MHz and SMCLK = DCO
 BCSCTL1 = CALBC1_16MHZ; // Set DCO
 DCOCTL = CALDCO_16MHZ;
 BCSCTL2= DIVS_0 + DIVM_0; // divider=1 for SMCLK and 1 for MCLK
}

void main(void)
{
WDTCTL = WDTPW + WDTHOLD;   // Stop WDT
clockConfig();

P1DIR |= 0x04;     // P1.2  output
P1SEL |= 0x04;     // P1.2  set to TA0.1 
TACCR0 = 7;        // PWM Period
TACCTL1 = OUTMOD_7;    // CCR1 reset/set
TACCR1 = 1;  // CCR1 PWM Ton -> duty cycle
TACTL = TASSEL_2 + MC_1;    // SMCLK, up mode
_BIS_SR(CPUOFF);      // Enter LPM0


If we hook an oscilloscope probe to P1.2 we can see this :



In the lower left rectangle you can verify that the signal frequency is correctly 2MHz.
In the next two pictures you can see the effect of changing the Duty Cycle value (via TACCR1) to 50% and to 75%.

Since the maximum clock frequency of the clock is 16MHz and we need at least an "up" value and a "down" value, it means that the maximum pwm frequency we can obtain is 16/2= 8MHz, as visible below. 


The signal appears heavily rounded mainly due to the way I connected the probe (non shielded cable, about 30cm long, definitely not the best setup to monitor a 8MHz signal).
This high frequency is not really usable for PWM purposes, unless you are happy with 3 duty cycle levels (0%,50%,100%).

This is because your Ton time is defined by a finite number of cycles, if you are working with a T defined by a large number in TACCR0 (thus increasing your period T and lowering your PWM frequency) you can probably have  a fine control over the duty clycle.

So it's a bit of trade off between maximum frequency and detailed control of the duty cycle, where to set the bar really depends on your application.
In many cases you will probably be happy with frequencies of few KHz and about 1% of Duty Cycle increments.

Let's make a numeric example :

Required F = 10KHz -> T = 1 / 10.000 s
Duty Cycle increments = 1% -> dT = 1 / 10.000 * 1 / 100 = 1 / 1.000.000 s

The easiest way to achieve this would be to set SMCLK to run at 1MHz, TACCR0 = 99 (100 cycles/period = 1MHz/100 = 10KHz) and TACCR1 between 0 and 99.

I deliberately chose the lowest possible value for SMCLK, any value equal or bigger than that would 4 do, i.e. SMCLK = 4MHz

-> TTACR0 = 4 * 100 -1 = 399;
-> TACCR1 between 0 to 399 with a minimum increment of 1/4% = 0.25%


Thursday, August 9, 2012

C2000 Launchpad - Code - First look

I finally managed to have my C2000 Launchpad up&running with CCS V5_2.

The process was not really smooth for me as I initially had double entries in controlSuite and the C2000 Launchpad examples were not showing up in CCS (but they were there if I opened controlSuite externally).

I had to uninstall everything (for controlSuite a manual delete of the files since there is no uninstall, or at least I was not able to find it).

On windows 7 64 bit few things did not work (i.e. CCS was not able to update etc) until I realized I had to run the install and later the software as Administrator.
That did the trick, both for controlSuite install and for CCS install / update.

Phew...
If you follow the instructions on the video available on the TI website, you can open the example from the Resource Explorer, load the project etc...

At step 3 I was not really sure which connection to choose, the XDS100V1 USB worked for me


Also, I switched back the 3rd dip switch to "high" since in the TI video by Trey German (thanks Trey, cool stuff! looking forward for more of your videos!) there is a mention to the fact that enables /disables the jtag debug interface.

So, compiled and debugged, no issues there.

Following the code you immediately realize, if you played before with the "little brother" MSP430G2 Launchpad, that the "piccolo" is totally another animal.

Code appears immediately mode "high level", as I expected, in line with what I found dealing with the Cortex M3 I played with.

Code seems quite "elegant" (not that I don't like the "hardcore" style of the MSP430 code, I actually love it) and probably designed to better fit in bigger applications.

This is the loop that manages the sequential blinking of the blue leds in the pre-installed demo


  //Scan the LEDs until the pushbutton is pressed
    while(GPIO_getData(myGpio, GPIO_Number_12) != 1)
    {       
        GPIO_setHigh(myGpio, GPIO_Number_0);
        GPIO_setHigh(myGpio, GPIO_Number_1);
        GPIO_setHigh(myGpio, GPIO_Number_2);
        GPIO_setLow(myGpio, GPIO_Number_3);
        DELAY_US(50000);

        GPIO_setHigh(myGpio, GPIO_Number_0);
        GPIO_setHigh(myGpio, GPIO_Number_1);
        GPIO_setLow(myGpio, GPIO_Number_2);
        GPIO_setHigh(myGpio, GPIO_Number_3);
        DELAY_US(50000);

        GPIO_setHigh(myGpio, GPIO_Number_0);
        GPIO_setLow(myGpio, GPIO_Number_1);
        GPIO_setHigh(myGpio, GPIO_Number_2);
        GPIO_setHigh(myGpio, GPIO_Number_3);
        DELAY_US(50000);

        GPIO_setLow(myGpio, GPIO_Number_0);
        GPIO_setHigh(myGpio, GPIO_Number_1);
        GPIO_setHigh(myGpio, GPIO_Number_2);
        GPIO_setHigh(myGpio, GPIO_Number_3);
        DELAY_US(500000);
    }

I would say it is definitely readable and you don't need to know the names of the registers to make sense of it.

It appears that they used a good naming convention where all the calls are prefixed with the name of the involved peripheral, which will be GPIO, ADC, CLK...

And check this :


    fid = fopen("scia","w");
    freopen("scia:", "w", stdout);
    setvbuf(stdout, NULL, _IONBF, 0);

Oh yeah, that's your serial port saying "hello" there.
How to configure the baud rate?


#if (CPU_FRQ_60MHZ)
    SCI_setBaudRate(mySci, SCI_BaudRate_115_2_kBaud);  

Want to send some bytes over the serial port?

void updateTemperature(void)
{
    // Restore cursor position
    putchar(0x1B);
    putchar('[');
    putchar('u');  
    printf("%d Celcius = Ref + %d ", currentTemp, (currentTemp - referenceTemp));   
}


Honestly I found really cool with the MSP430G2 USCI to be able to set the parameters for modulation etc... but, ok, this looks way easier.
All this is possible with the support of  various header files, there is one for each peripheral

Overall, first impression : thumbs up!


Tuesday, August 7, 2012

MSP430G2 - Timers and Low Power Modes

Like all the MCUs the MSP430 has timers.
Timers are extremely useful in various applications, a typical usage is to generate an interrupt every X micro / milli / seconds to perform a given task.

I could create a very simple project to experiment with the timer, but I decided it  would have been nicer to add some timer functionality to my last experiment, about SPI communication.

In the last experiment I sent commands to a digital pot (MCP4131) via the SPI line, moving it's wiper to generate a low frequency (~1Hz) triangle waveform.

The idea was to send a value, then delay for a number of cycles (20000), then send the next value etc.
That's ok for a simple experiment, but it is definitely not best practice.
In general you don't want the MCU just counting cycles since in real applications you may have other activities to perform in parallel and their duration would affect your delay.
Even if you are not doing anything else, it would be better to set the MCU in sleep mode (low power) and wake it up just  in time to send the new wiper value to the digital pot.

That's were timers come in help, particularly with the MSP430G2 MCUs.
These devices are designed to support very low power (sleep) modes while keeping some functionality active.
The following picture shows the typical current used by the MSP430 mcu (Fxxx for the specific chart, but we can assume a similar profile for the Gxxx ones), you can see how it drops when entering low power modes (LPM).


Ideally we would like to select the most conservative mode (LPM4), however we need to check that the set of functionality we need are active in the mode we choose.


First thing to understand is that in LPM the CPU is NOT active.
It means that code placed in a normal loop in the main function would not be executed.
So, how can we still "do something" with the CPU?
It will respond to interrupts, including those generated by timers, then it will execute the code of the interrupt functions we will provide.

The second important thing to check is that some clock systems are turned off in LP modes and in some cases also the digital oscillators could be turned off.

In all LP modes the Master Clock (MCLK) is not active, it makes sense as the CPU is off too.
However we are using SMCLK for the SPI communication, so we will need that one on.
Moreover our source oscillator is  DCO, we will need that one active too. 

Using the table 2-2 (from the MSP430 user guide) we identify LPM0 as the only LP mode that supports what we need.

Also, we will need a timer, and timers also use a clock, we will make sure that the selected one is going to be SMCLK.

Our setup procedure will be :

//Timer
void setupTimer()
{
 TACCTL0 = CCIE;   // Timer A Capture / Compare 0 interrupt enabled
 TACCR0 = delCycles; // cycles to count
 TACTL = TASSEL_2 + MC_2;  // SMCLK, contmode
}

The Timer A Capture / Compare ConTroL 0 (TACCTL0) is used to enable the interrupt generation.
The Timer A Control Register 0 (TACCR0) will hold the number of cycles needed to generate an interrupt (it's a 16 bit value ->0x0000 to 0xFFFF).
Finally the TACTL register selects which clock source is used, an eventual divider and the timer mode.

TACTL register

While the registers TACTL and TAR -the time counter- are related to the timer itself, the TACCxxx registers are related to the capture / compare devices associated to that timer.
There are up to 3 CC devices in timer A (more on timer B) and each one of them can be configured to trigger at a different counter level using their register TACCRx.
They can also be configured in different ways using their TACCTLx  control registers, in this first experimet we will just enable for them the Interrupt. 

There are four timer modes, probably for our purpose anyone of them could do, but we would need to adjust our code accordingly.
Probably mode 1 is the easiest to use, followed by mode 2.


Mode zero is just used to stop the timer, should not need any further explanation.

Mode 1 "Up" tells the timer to count up to a value stored in TACCR0, fire an interrupt if enabled and the restart from zero.

Mode 2 "Continuous" instead keeps counting up to 0xFFFF but fires the eventual interrupt when the value reaches what we store in TACCR0.

Mode 3 "Updown" acts similarly to Mode 1, but, instead of resetting when reaching TACCR0, it starts counting down to zero.

  

Since the wiper level will not be managed in a loop now, we need two global variables, one holding the wiper level and one holding the direction of the ramp, so that we can properly increment / decrease the value.
I added a third variable to hold the number of (SMCLK) cycles to wait.

 unsigned char potLevel;
 unsigned char potDir;
 unsigned int delCycles;
...
void main(void)
{
// setup
 WDTCTL = WDTPW + WDTHOLD;
 clockConfig();
 pinConfig();
 spiConfig();
 potLevel=0;
 potDir=1;
 delCycles = 20000;
 setupTimer();
 _BIS_SR(LPM0_bits + GIE);  // Enter LPM0 w/ interrupt
}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
  setPotValue(potLevel);// SPI communication
  potLevel += potDir; // increment or decrease
  if (potLevel>127) // potLevel is unsigned, below zero will be 255 
  {
potDir = -potDir;
potLevel +=potDir;
  }
                    // set the next interrupt delay
  TACCR0 += delCycles; // we are in continuous mode! (Mode 2)
}

The main function is updated to initialize the global variables and to set up timer A, plus an interrupt function is created to handle the timer interrupt. 

Finally the instruction _BIS_SR(LPM0_bits + GIE) sets the CPU in LPM0 with Interrupts enabled.
Notice that we don't need anymore a loop to handle the execution, the CPU will be disabled and will be awaken by the interrupt generated by Timer A.

Timers can also be used for different purposes, with these MCUs they support also PWM functionality with direct output to IO pins.
I will cover these aspects in a future post.

Note : all the images are taken from the MSP430 user guide, by Texas Instruments.
The document can be downloaded here.