Search Microcontrollers

Showing posts with label C2000 Piccolo. Show all posts
Showing posts with label C2000 Piccolo. Show all posts

Tuesday, October 16, 2012

C2000 launchpad - code skeleton

Maybe I am just lazy, maybe laziness is a bit "the geek way".

Each time I start a new project I like to copy from a skeleton, some kind of template instead of typing everything from scratch.
The real reason, I think, is that in most of the cases I cannot recall how to start and with the C2000 Launchpad I always forget something, so I created a file with all the steps and code snippets.

I am posting it here, just in case it might turn useful for others.

First, in CCS start a new project (FIle, new Project, CCS Project )


Then  set the basic project properties (see the highlighted parts)


Some tweaks are then needed in the project properties after the project is created, right click the project name in the project explorer, select properties and add the laiunchpad specific location for the include files.
You may need to adjust that path according to your ControlSuite install dir


Finally add the library


You are now good to go, open your main.c file and copy & paste the following skeleton

Update : Make sure you select the correct linker command file, by mistake I selected 28027 instead of F28027 and spent some time to figure out why the program would not fit in memory according to the linker.



//-------------------------------------------------------

#include <stdio.h>
#include <file.h>

#include "DSP28x_Project.h"     // DSP28x Headerfile

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

#include "f2802x_common/include/flash.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pie.h"
#include "f2802x_common/include/adc.h"
#include "f2802x_common/include/sci.h"
#include "f2802x_common/include/sci_io.h"


extern void DSP28x_usDelay(Uint32 Count);

// you probably need these
CPU_Handle myCpu;
PLL_Handle myPll;
WDOG_Handle myWDog;
CLK_Handle myClk;

// these are optional
ADC_Handle myAdc;
FLASH_Handle myFlash;
GPIO_Handle myGpio;
PIE_Handle myPie;
SCI_Handle mySci;




void setup_handles()
{

    myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
    myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));
    myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));
    myCpu = CPU_init((void *)NULL, sizeof(CPU_Obj));

    myFlash = FLASH_init((void *)FLASH_BASE_ADDR, sizeof(FLASH_Obj));
    myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));
    myPie = PIE_init((void *)PIE_BASE_ADDR, sizeof(PIE_Obj));
    mySci = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj));
    myAdc = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj));


}

void init_system()
{
  WDOG_disable(myWDog);
  (*Device_cal)();
  CLK_setOscSrc(myClk, CLK_OscSrc_Internal);
  PLL_setup(myPll, PLL_Multiplier_12, PLL_DivideSelect_ClkIn_by_2);

  PIE_disable(myPie);
  PIE_disableAllInts(myPie);
  CPU_disableGlobalInts(myCpu);
  CPU_clearIntFlags(myCpu);
#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
#endif
}

void main()
{
  setup_handles();
  init_system();
}
//---------------------------------------------

Note that this is just a skeleton, you may need to remove parts you may not need such as include files, handles etc (unless the linker is smart enough to get rid of them, which I think it's something we might expect).

Also, you will need to add specific peripheral initialization according to your needs and... your program.






Saturday, October 13, 2012

C2000 Piccolo - Serial communications

I was actually tempted to dig into the ADC functionality, however serial communications seemed to be a higher priority thing to learn.
Why?
Let's face it : we (normally) do not have a screen where we can output some debug information, our best bet with microcontrollers is the UART and use a terminal program on a PC to visualize some data.

As I already anticipated in a previous post, the serial interface is handled quite at "high level" with the C2000, in a similar way you would deal with it on a PC.
First of all , in the C2000 the uart is referred a SCI (Serial Communication Interface) and since there is a single module "A", it normally becomes SCI-A or SCIA.

I am starting to look around the SCIA using the demo program tha comes with the c2000 Launchpad, unfortunately that one does not cover the RX part, and therefore all the interrupt handling is not covered either.
We will need to look somewhere else for that.

One interesting thing to notice is that the uart has double buffered RX and TX functions which is kind of neat.
Unfortunately it looks like that a single hardware uart is supported in the piccolo (some devices may support  SCI-B), which is a pity since at least two serial ports are always handy, typically you would use one to communicate with a device and the other one to debug whatever you need with a PC.
At least this is what I normally do.
It should be possible and quite easy to setup a software uart if needed, I hope.

Another thing I noticed is that it looks like there is no hardware flow control functionality provided, no big deal probably as a couple of gpio lines could be eventually used for that, after all that's the beauty of dealing with an mcu.

So, this said, we expect to have only two pins to deal with :   RX and TX.
In fact they are accessible from the Launchpad expansion headers

(from the C2000 Launchpad pdf, by TI)


SCIRXDA (RX) is available on J1.3 (mode 1), J2.2 (mode 2), J2.9 (mode 2)
SCITXDA (TX) on J1.4 (mode 1), J1.7 (mode 2), J2.3 (mode 2)

J1.4 (mode 1) means header J1, pin 4 and MUX mode set to 1

Now, the pdf that came with the Launchpad explains that the board has an FTDI interface to which, by default, the serial communications are redirected.
That makes them available through the USB port.
The Switch S4 connects and disconnects the uart from the ftdi, so, normally, if you want to use the ttl uart signals (3.3V tolerant) you are supposed to switch S4 so that they are isolated from the USB.

But then I am wondering :
According to the Launchpad schematics, S4 connects and disconnects J1.3 and J1.4   (GPIO28 and GPIO29), which would mean that if we use another combination (i.e. the J2.2/3 option) normally we would be isolated from the USB.
That's something I will probably test later.

Let's start simple and imagine we want to connect the SCIA to the usb/ftdi, then we will have the S4 switch set to ON and configure properly the GPIO28 and 29 pin on the mcu.

The demo program has it all in this case :


    GPIO_setPullUp(myGpio, GPIO_Number_28, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_29, GPIO_PullUp_Disable);

    GPIO_setQualification(myGpio, GPIO_Number_28, GPIO_Qual_ASync);

    GPIO_setMode(myGpio, GPIO_Number_28, GPIO_28_Mode_SCIRXDA);
    GPIO_setMode(myGpio, GPIO_Number_29, GPIO_29_Mode_SCITXDA);


The RX is going to be configured as input (implicitly, that's done automatically when setting the pin to uart mode).
To avoid floating signals the pullup  resistor is enabled.
TX is obviously output, therefore pull resistors are not used.
The setQualification part is related to synchronizing and qualifying the signal with the internal clock.
I will not enter too much in the details here, but let's just say that if a signal uses a clock that is not defined internally (this is the case for the RX signal whose clock depends on the other device on the communication line), then it needs to be synchronized with the internal clock to detect the gap with it.
A number of samples (configurable) is taken and a high frequency clock (SYSCLK or a divided value) is used to assess the gap.
The process is described in the 2802X_system manual.

The final two lines set the MUX  mode for GPIO28 and 29 pins, the GPIO_28_Mode_SCIRXDA and GPIO_29_Mode_SCITXDA are two enum constants (value = 1).

If instead we would have preferred to use the J2 option (GPIO19 = RX, GPIO12 =TX) the needed code would have been :


    GPIO_setPullUp(myGpio, GPIO_Number_19, GPIO_PullUp_Enable);
    GPIO_setPullUp(myGpio, GPIO_Number_12, GPIO_PullUp_Disable);

    GPIO_setQualification(myGpio, GPIO_Number_19, GPIO_Qual_ASync);

    GPIO_setMode(myGpio, GPIO_Number_19, GPIO_19_Mode_SCIRXDA);
    GPIO_setMode(myGpio, GPIO_Number_12, GPIO_12_Mode_SCITXDA);


And presumably the S4 switch would not affect at all, however, before connecting a ttl serial device to them, I would make sure that GPIO28 and 29 are NOT configured as SCIA at the same time (!!).

Now that we have the pin configured properly, let's take a look to the function that configures the SCIA peripheral (still from the demo software)


 mySci = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj));

This line initializes a SCI handler, which will be used later.

void scia_init()
{
    CLK_enableSciaClock(myClk);

We also initialized and configured properly the clock (check this other post for details) , now we are using the myClk handler to enable the clock for the SCI peripheral

    SCI_disableParity(mySci);
    SCI_setNumStopBits(mySci, SCI_NumStopBits_One);
    SCI_setCharLength(mySci, SCI_CharLength_8_Bits);
    
    SCI_enableTx(mySci);
    SCI_enableRx(mySci);

Ok, this part is quite obvious,  we are using the classic 8-N-1 setup and we want both RX and TX channels enabled.

    SCI_enableTxInt(mySci);
    SCI_enableRxInt(mySci);

Also we think it is cool to have interrupts on both RX and TX buffers, then we want to set the Baud Rate.
At this point, you realize that the way these libraries have been created allow you to perform all the needed configuration without knowing much about the internals of the device.
... but some details cannot hurt.

The base clock of the SCI is the low speed clock (LSPCLK), then a 16 bit register (BRR) is provided to configure a divider.
That allows up to 65535 different BR configurations, the formula is the following :


By default the LSPCLK is set to SYSCLK / 4 ( 60MHz/4 in our case = 15MHz)
According to the formula, assuming BRR = 15
-> BR = 15.000.000 / (15 +1)*8 = 15.000.000 /128 = 117.2K  which is not exactly 115.2K  but close enough.
Using BRR = 16 we would have 15.000.000 / 136 = 110.3K and using BRR = 14 : 125K
So, finally, BRR = 15 is the best option

    // Configured for 115.2kbps
#if (CPU_FRQ_60MHZ)
    SCI_setBaudRate(mySci, SCI_BaudRate_115_2_kBaud);    // value is 15
#elif (CPU_FRQ_50MHZ)
    SCI_setBaudRate(mySci, (SCI_BaudRate_e)13);
#elif (CPU_FRQ_40MHZ)
    SCI_setBaudRate(mySci, (SCI_BaudRate_e)10);
#endif

Obviously we need a different divider if the CPU is not set to 60MHz, but hey, you have the formula, you can do the math :)
The following code enables the fifo buffers for both the RX and TX channels, then it enables the TX fifo interrupt, but not the RX one.
That's because, I suppose, the sample program is not meant to receive any data, in a real world application we would probably need that one too.
Despite the fact that the TX interrupt is enabled, there is no interrupt handler function in the demo program.
I assume the interrupt is used internally by the library in order to manage properly the output buffer.

    SCI_enableFifoEnh(mySci);
    SCI_resetTxFifo(mySci);
    SCI_clearTxFifoInt(mySci);
    SCI_resetChannels(mySci);
    SCI_setTxFifoIntLevel(mySci, SCI_FifoLevel_Empty);

    SCI_resetRxFifo(mySci);
    SCI_clearRxFifoInt(mySci);
    SCI_setRxFifoIntLevel(mySci, SCI_FifoLevel_4_Words);

Finally, after clearing the interrupts, the buffers etc, a priority is set and the module enabled

    SCI_setPriority(mySci, SCI_Priority_FreeRun);
    SCI_enable(mySci);
    return;
}



checking in the header file it looks that the following priorities are available :

 
     SCI_Priority_Immediate=(0 << 3),    //!< Denotes an immediate stop
    SCI_Priority_FreeRun=(1 << 3),      //!< Denotes free running
    SCI_Priority_AfterRxRxSeq=(2 << 3)  //!< Denotes that a stop after the current receive/transmit sequence

That completes the hardware configuration, on the software side, the port is dealt with similarly to a stream (or file) :


    status = add_device("scia", _SSA, SCI_open, SCI_close, SCI_read, SCI_write, SCI_lseek, SCI_unlink, SCI_rename);
    fid = fopen("scia","w");
    freopen("scia:", "w", stdout);
    setvbuf(stdout, NULL, _IONBF, 0);

First a "device" named scia is added, then it is opened for output and finally redirected to the standard output device (stdout).
From now on, each write to stdout will actually be directed to the uart :

   putchar(0x1B);

This was a very high level first look into the serial communications of the C2000, will come back in the future with some more details and some experiments.



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.






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!