This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

New to the C2000

Other Parts Discussed in Thread: TMS320F28027, CONTROLSUITE

Hi, I am taking a digital control class and have a project in which I have to control the yaw rotation of a RC helicopter through motor-speed control utilizing a C2000 MCU with the TMS320F28027 LaunchPad XL. I am having difficulties while coding the C2000, it's my first time coding a MCU and many of the examples are a bit tough to figure out. For example(this is from the EPwmDCEventTripCompare):

// Global variables used in this example
uint32_t EPwm1TZIntCount;
uint32_t EPwm2TZIntCount;

In here, are the variables EPwm21TZIntCount and EPwm2TZIntCount specific names that refer to specific pins in the MCU or are they chosen arbitrarily.

Next question: When variables are initialized I see something like:

CLK_Handle myClk;

there are a couple more like COMP_Handle, Gpio_Handle, and so on. What is the XXX_Handle thing? Each variable made(myClk, myGpio, and so on) is later initialized as a handle with a line as follows:

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

What does initializing a handle mean? Finally(for now), where can I find a list of general commands to use? I see so many in the program example that I have never seen before and that are directly related for the C2000, is there a guide for really new people? I have tried figuring out the examples and watched the tutorials of the one-day workshop in youtube and I'm still having a really difficult time.

Thank you for all and any help in advance. 

  • Hi,

    In here, are the variables EPwm21TZIntCount and EPwm2TZIntCount specific names that refer to specific pins in the MCU or are they chosen arbitrarily.

    They are custom names and can be altered.

    Secondly, when using launchpad please follow the user guides thoroughly!

    These guides can be found here: http://www.ti.com/product/tms320f28027 under user guides section.

    Here you'll get all the detailed info about the peripherals.

    Regards,

    Gautam

  • Hello Efrain,

    I understand your difficulties.

    About XXX_Handle things, they are writing as the pointer to its objects (forgive my mistake about saying it as OOP previously, please don't get confuse).

    For example: CLK_Handle handles its object called CLK_Obj that you can find the declaration in clk.h.

    Usually the object contains all of the necessary registers.

    The other example is GPIO_Handle, that handles its object called GPIO_Obj that you can find the declaration in gpio.h.

    You can see all of the registers of GPIO are mentioned there one-by-one sequentially (arranged by its address).

    This what the initial (XXX_Init) is important. Because it will mention the base address of each object.

    For example, for CLK_Obj (start from XCLK), the base address is 0x00007010.

    typedef struct _CLK_Obj_
    {
        volatile uint16_t   XCLK;            //!< XCLKOUT/XCLKIN Control  --> the address is 0x00007010 (mentioned by CLK_Init)
       ...
        volatile uint16_t   PCLKCR3;      //!< Peripheral Clock Control Register 3 --> the address is not mentioned, but by counting it from XCLK address, you can find that the address is 0x00007020
    } CLK_Obj;

    typedef struct CLK_Obj *CLK_Handle;

    And for GPIO, the base address is 0x00006F80.

    You can find all of the peripheral address in the datasheet.

    Hope this helps you to understand more.

    Good luck!

    Best regards,

    Maria

  • Efrain,

    Welcome to the world of embedded programming! Since this is a class project I'm guessing you're short on time, but if you get a chance, brush up pointers in C. A deeper knowledge of pointers is *very* helpful for MCU programming.

    Not sure how much you know already, so let's start with the basics. In embedded programming we're much more concerned about CPU-specific details than in normal C application programming. Often we care about the actual size of data types, since that's what we get from hardware. (Registers are 16 or 32 bits, not "short int" or "unsigned long int" bits.) Usually we'll use typedefs to define specific data sizes. Section 6.4 of the compiler manual (SPRU514F) lists the sizes of the C data types:

    char = 16 bits <-- Very unusual! Most CPUs have 8-bit chars, but the C28x CPU is really a 16-bit DSP.
    int = 16 bits
    long = 32 bits
    long long = 64 bits
    pointer = 22 bits <-- In the large memory model. Also unusual. 32 bits is more common.

    So we could define specific types like this:

    typedef int signed_16bit_int;
    typedef unsigned int unsigned_16bit_int;
    typedef long int signed_32bit_int;
    typedef unsigned long int unsigned_32bit_int;

    and then declare variables like this:

    signed_16bit_int s1, s2, s3;
    unsigned_32bit_int u1, u2;

    This is so common that C now does it for us. If you #include <stdint.h>, you get types like int16_t (signed 16-bit integer) and uint32_t (unsigned 32-bit integer). Uint32s are very common since many registers are 32 bits.

    (So where's #include <stdint.h> in the example code? It gets #included in F2802x_Device.h, which is #included in DSP28x_Project.h, which is #included in the main C file for the example. Both of those headers are in the controlSUITE\development_kits\C2000_Launchpad directory. The header files system is complicated, but #including DSP28x_Project.h should get you almost everything you need.)

    Okay, on to registers. I'm not familiar with the launchpad library specifically, but I can tell you a few things. The goal here is to read and write values (integers) to and from hardware registers. The registers are memory-mapped. (Let me know if you need an explanation for that.) So to access them, we need to make the CPU access specific memory addresses. There are two standard ways to do this:

    1. Make a pointer to the memory address.
    2. Tell the linker to put a variable at that memory address.

    Let's talk about the first method. We could use hardcoded numerical addresses everywhere:

    *(uint16_t *)0x701C |= CLK_PCLKCR0_ADCENCLK_BITS; //Enable ADC clock via the PCLKCR0 register

    but that's error prone and hard to read. We could #define names for every register:

    #define CLK_PCLKCR0 (*(uint16_t *)0x701C)
    CLK_PCLKCR0 |= CLK_PCLKCR0_ADCENCLK_BITS;     //Atmel does this in their 8-bit MCU libraries

    but that gets clunky, especially if you have several copies of a module. Three ADCs would mean three sets of constants used in three sets of functions, for instance. So in big MCUs, it's not ideal. A better option is to define the structure of the register group itself, without the absolute address. That's what the Launchpad software is doing:

    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've told the compiler where the clock registers are relative to each other. To get the right addresses, we make a pointer to the register frame:

    CLK_Obj *clk = (CLK_Obj *)CLK_BASE_ADDR;     //Pointer to address 0x7010

    This lets us access the registers as through they were part of a normal C data structure in memory:

    clk->PCLKCR0 |= CLK_PCLKCR0_ADCENCLK_BITS;     //OR the register at address 0x7010 with 1<<3 (0x0008)

    which is precisely what CLK_enableAdcClock() does. They just use a typedef for the CLK_Obj pointer in the function calls. (Typedefs are a running theme here, as you can tell.)

    The Launchpad code refers to these register structure pointers as "handles". A handle is a reference to some software or hardware resource. For example, standard C uses file handles:

    FILE *fileHandle;
    fileHandle = fopen("somefile.txt", "w");
    fputs("Text to be written", fileHandle);
    fclose(fileHandle);

    The purpose of a handle is to let you access a specific resource (e.g. one file out of many) without needing a low-level description of where/what it is. This lets you mostly ignore hardware details when you use higher-level functions. In the Launchpad software, a handle is just a pointer to the register frame. The XXX_init() functions assign the address (first parameter) to the pointer (return value) with some error checking based on the structure size, probably to make sure you copied and pasted correctly. :-)

    I know this all seems like black magic when you first work on it (it sure did to me!), but I promise it all makes sense and there's not all that much to it once you get going. If there's anything else you'd like me to explain, please feel free to ask.

    Good luck!



  • Very well explained, Adam!

    Regards,

    Gautam

  • Thank you everyone,

    The answers have definitively cleared up a lot of my doubts.

    Its appreciated very much.

  • Hi,

    Just thought I would add to this as fairly new to the C2000, but have been using the MSP430 and also the old LM3S Stellaris boards.

    I am used to changing all the registers on the MSP430 and seeing the C2000 software confused me at first (Still don't fully understand all the pointer magic).  From using the Stellaris LM3S I found the Peripheral Driver Library PDF invaluable, and there is one for the C2000 as well!

    It gives a good explanation on the programming model, in chapter 2 (page 7 and 8).  You can find it by navigating through the control suite software (as per the image below).  I did also look via the documentation searches for the C2000 but couldn't find it, this document should be front and center in my opinion as invaluable for anyone new to the C2000.

    Cheers,

    Ant