Jump to content
Vin255

Understanding ARM based controller (stellaris) through driverlib or through registers

Recommended Posts

As far as the license issues the more restrictive license is hold-over from previous version(s) of the libraries.  (Used to be they were all like that.)

TI has been opening them up, at least for the driverlib.  (I don't know their intentions for the example programs.)

 

Here is an earlier discussion of the licensing, and TIs cleanup efforts.

http://forum.stellarisiti.com/topic/339-stellarisware-license-vs-open-projects/

 

Here is another effort at creating startup and linker scripts.

http://forum.stellarisiti.com/topic/327-stellaris-launchpad-gcc-makefile-startup-file-and-linker-script-bsd

 

I am also interested in a startup file that doesn't need to be customized for each project.

Share this post


Link to post
Share on other sites

The approach I'm taking will probably only work with arm-gcc (maybe CodeSourcery too).  It uses an external MCU-specific file to provide the contents of the vtable so the startup file is shared,  and the same external memory.ld file I used in MSP430 so the linker script is shared.  It uses CMSIS 4.0 standard global handler names, instead of TI's names; driverlib/sensorlib should work, but the TivaWare examples will probably need to be edited.  It also sucks in more of the standard arm-gcc CRT infrastructure than TI's setup, increasing base code size by about 1 kiB, which is something that bothers me and I'm investigating to see if there's value there.

 

In any case, I want this to work with EFM32, Tiva-C, and the CC2538, so that's the requirement I'm working towards.  At the moment I have no evidence it can't be done, but I won't push anything out until I have confirmed solutions on those three platforms, which might be a week or two since I'm finding it really demotivating to have to keep spending time fixing up infrastructure instead of doing something cool.  (Control issues?  No, I don't have control issues....)

Share this post


Link to post
Share on other sites

Hello all,

The posts here indicates that people do have lot of knowledge and are willing to help.  :)  I too have lot to learn.Long way to go. 

I had 2 questions which is related to my initial query of programming the registers instead of using API:

 

1. in stellaris peripheral driverlib pdf, they have mentioned that in inc/lm4f120h5qr.h lot of address are defined for direct register access.

for ex: 

#define GPIO_PORTA_DATA_R       (*((volatile unsigned long *)0x400043FC))

Even bit masks are defined.I know APIs make things easy,safe and faster but I also wanted to learn the register level programming in parallel so that a good understanding of ARM cortex M4 is obtained since this is my first experience with an ARM controller. And also, APIs are not efficient in size and speed.

 

 

2. I know that to program a peripheral lot of registers have to be programmed in a specific order. My plan is to understand this order by going through the APIs and then trying to repeat the same through the registers. Does it make sense?

 

Thank you

regards

Vin255

Share this post


Link to post
Share on other sites

1. in stellaris peripheral driverlib pdf, they have mentioned that in inc/lm4f120h5qr.h lot of address are defined for direct register access.

for ex: 

#define GPIO_PORTA_DATA_R       (*((volatile unsigned long *)0x400043FC))

Even bit masks are defined.I know APIs make things easy,safe and faster but I also wanted to learn the register level programming in parallel so that a good understanding of ARM cortex M4 is obtained since this is my first experience with an ARM controller. And also, APIs are not efficient in size and speed.

TI's APIs aren't efficient in size or speed; e.g. UARTIntClear(UART0_BASE, value) is a function call, even if you use the ROM implementation. Other solutions aren't quite so bad; the equivalent in EFM32-land is:

  USART0->ICR = value;
which is a direct write through a pointer to the struct that lays out the registers belonging to the peripheral instance.

 

TI doesn't even provide a struct declaration for their peripheral registers; they have macros that expand to each individual register (as you showed), and they make you include the specific device header for that even though every device in the TM4C123 line shares the same layout for all its peripherals.

 

What really gripes me is that the CMSIS headers for TM4C, which I found on github, do provide this much nicer interface, but it seems nobody at TI is interested in making them available.

 

2. I know that to program a peripheral lot of registers have to be programmed in a specific order. My plan is to understand this order by going through the APIs and then trying to repeat the same through the registers. Does it make sense?

Well, give it a try. Personally I'm finding TI's non-CMSIS headers to be a mess, and I'd probably just stick with driverlib until I had to produce something that I wanted to be efficient, at which point I'd use the black-market CMSIS headers.

 

(I mean, I'd really like to like TivaWare, but the more I see of it the more disgusted I become.)

Share this post


Link to post
Share on other sites

Hi Pabigot

    yes, I agree about struct-pointer. I have downloaded the CMSIS files from the link and will have a look at them later.

Ok, I will stick with the driverlib for now.

 

In E2E forum they didnt even reply! Hope some one there wakes up to this confusion.

 

The only reason why I want to program through the registers is that it will give good understanding about the complexities of ARM architecture and If we learn deeply one ARM architecture then other variants of it are almost similar.

thanks.

 

Regards

Vin255

Share this post


Link to post
Share on other sites

So I've played with the github CMSIS stuff enough to know it can be made to work, though there are bugs in it (the GPIOA_Type structure is wrong for the DATA field, which should be an array of 256 words at the start of the struct, rather than a single word after 255 words of padding). Also a lot of defines for bit fields within the registers are missing.

 

Here's a taste of some examples translating code from the ek-tm4c123gxl uart_echo demonstration from driverlib to CMSIS:

  // Get and clear the interrrupt status, then loop while the RX
  // FIFO has data
#if CMSIS - 0
  UART0->ICR = UART0->MIS;
#define UART_FR_RXFE 0x10
  while(! (UART_FR_RXFE & UART0->FR)) {
    UART0->DR = UART0->DR;
    GPIOF->DATA[GPIO_PIN_2] = GPIO_PIN_2;
    SysCtlDelay(SysCtlClockGet() / (1000 * 3));
    GPIOF->DATA[GPIO_PIN_2] = 0;
  }
#else
    {
      uint32_t ui32Status;
      ui32Status = ROM_UARTIntStatus(UART0_BASE, true);
      ROM_UARTIntClear(UART0_BASE, ui32Status);

      while (ROM_UARTCharsAvail(UART0_BASE)) {
        ROM_UARTCharPutNonBlocking(UART0_BASE,
                                   ROM_UARTCharGetNonBlocking(UART0_BASE));
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
        SysCtlDelay(SysCtlClockGet() / (1000 * 3));
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
      }
    }
#endif
      /* Write next char to UART */
#if CMSIS - 0
#define UART_FR_TXFF 0x20
      /* NB: This one waits for tx space */
      while (UART_FR_TXFF & UART0->FR) {
        /* spin */
      }
      UART0->DR = *pui8Buffer++;
#else
      /* This one does not, and can drop chars */
      ROM_UARTCharPutNonBlocking(UART0_BASE, *pui8Buffer++);
#endif
    // Set GPIO A0 and A1 as UART pins.
#if CMSIS - 0
    GPIOA->PCTL |= 0x11;
    GPIOA->AFSEL |= GPIO_PIN_0 | GPIO_PIN_1;
    GPIOA->DR2R |= GPIO_PIN_0 | GPIO_PIN_1;
    GPIOA->DEN |= GPIO_PIN_0 | GPIO_PIN_1;
#else
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    //PinTypeUART is equivalent to:
    //GPIODirModeSet(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_DIR_MODE_HW);
    //GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
#endif
    // Enable the GPIO pins for the LED (PF2).
#if CMSIS - 0
    GPIOF->DR2R |= GPIO_PIN_2;
    GPIOF->DEN |= GPIO_PIN_2;
    GPIOF->DIR |= GPIO_PIN_2;
#if 1
    { /* HardFault exception raised if there isn't a delay here */
      volatile uint16_t dd = 0;
      while (--dd);
    }
#endif
#else
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);
    // Above is equivalent to:
    //GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
    //GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_DIR_MODE_OUT);
#endif
That last one is interesting; may be that driverlib's slowness obscures a bug (or may be that they're relying on it being slow to avoid having to put in some delay that I don't know about).

 

The CMSIS version, using my build environment, drops from 3024 code bytes to 2812 code bytes (the original with TI's ldscript is 1720 bytes, but I've got some extra stuff in mine to help diagnose problems).

 

If TI would provide a supported CMSIS environment, I'd definitely use it instead of driverlib. Which, as @@bluehash pointed out earlier, is probably part of why they don't provide CMSIS.

Share this post


Link to post
Share on other sites

I'm just wondering why using the register structures seems any more portable than just using the TivaWare. Neither of them is portable or useful any place except in combination with TI's chip and peripherals.

Share this post


Link to post
Share on other sites

Hi,

I am speaking for myself, not part with any brand - each approach has its own advantages and disadvantages. You may use whatever you need or think is more appropriate to your application.

CMSIS is more permisive in terms of its licence - you may use it in both commercial and non-commercial applications. However, its price, as noted by @@pabigot, is the code size and of coarse the execution time. This expression:

GPIOA->AFSEL |= GPIO_PIN_0 | GPIO_PIN_1;

for instance is of the type read-modify-write sequence, so it induces some penalty in code size and of coarse execution time, since there are three operations to make, while the the HWREG used by TI is of type write-only, since uses bit-banding, which allows to change any bit, and this is done in a single write cycle. This may become obvious in time-constrained applications, where you need to optimize the execution time of interrupts, otherwise could be unimportant.

As for portability of structures - yes, maybe, but this stop here, since there is not any true portability between different brands: for instance TI makes GPIO ports 8-bit wides, ST makes them 16-bits and NXP makes them 32-bits. Not the same also for other peripherals implemented. I checked the CAN module implementation on TI and compared to that of NXP, for Cortex-M4. While the general structure looks the same, NXP has an extra register more than TI, not speaking about the register names are different.

And despite these, if there are some libraries ready-made why not to use them?

L

Share this post


Link to post
Share on other sites

@pabigot: oh, ok, I wish the CMSIS was supported by TI. It needs lot of experience and modifications to use directly the CMSIS. I will stick with driverlib initially.

 

@ rickta59 an @@Lyon : I agree with both of you that there are lot of differences between different brands as you have pointed out. 

 

The reason why I want to try the register programming in parallel to driverlib is to understand the architecture of cortex m4.

 

There are technical ref manual for the cortex architecture at ARM & TI website, but only by programming a device(a particular implementation of that architecture, for ex: stellaris LP)  by register we can understand and appreciate the complexity of the architecture.

Here, I have bought stellaris launchpad from online store just to get in-depth knowledge of M4. 

 

In future, if I want to program any other controller then I will just be using only the driver libs but here I want to try register level also since this is my first ARM controller programming.

 

Thanks for your views:

 

regards

Vin255

Share this post


Link to post
Share on other sites

This expression:

GPIOA->AFSEL |= GPIO_PIN_0 | GPIO_PIN_1;
for instance is of the type read-modify-write sequence, so it induces some penalty in code size and of coarse execution time, since there are three operations to make, while the the HWREG used by TI is of type write-only, since uses bit-banding, which allows to change any bit, and this is done in a single write cycle.

 

But this is not true. AFSEL is a single register that is not accessed using bitbanding: the TivaWare code that implements the equivalent to the CMSIS operation is:

   HWREG(ui32Port + GPIO_O_AFSEL) = ((ui32PinIO & 2) ?
                                      (HWREG(ui32Port + GPIO_O_AFSEL) |
                                       ui8Pins) :
                                      (HWREG(ui32Port + GPIO_O_AFSEL) &
                                       ~(ui8Pins)));
Which is just as much an RMW operation as the CMSIS version.

 

Bit-banding is relevant for operations on a GPIO DATA register, which is (in the CMSIS-based modification I used) declared as an array, and:

  GPIOF->DATA[GPIO_PIN_2 | GPIO_PIN_6] = 0xFF
does use bit-banding and only affects pins 2 and 6.

 

CMSIS is more permisive in terms of its licence - you may use it in both commercial and non-commercial applications. However, its price, as noted by @pabigot, is the code size and of coarse the execution time.

My example showed that CMSIS is smaller and faster, because TivaWare involves function calls into ROM for operations that should be inlined. Thus the price of using the vendor's custom library is code size and execution time.

 

I absolutely think there is a value in vendor-provided functions that go beyond simple peripheral register manipulations to provide higher-level functions. Much of driverlib does not meet this level of functionality, and is simply bloated and obscure.

 

I didn't mean to imply that use of a CMSIS-based peripheral gives you portability across Cortex-M chips; it certainly does not, as peripherals are explicitly open to variation by the silicon vendor. What it does give you is a common programming model that is re-usable: you manipulate Cortex-M peripheral and MCU registers with C language expressions involving pointers to mapped registers, not through vendor-specific function calls. The available registers, and how they're used, are specific to a particular peripheral implementation, but that variation is not nearly so large as whether it's spelled GPIO_PinOutSet(port, pin) as in emlib or GPIOPinWrite(port, pin, value) as in TivaWare.

 

And despite these, if there are some libraries ready-made why not to use them?

This is the heart of a discussion on this topic elsewhere. The answer is that in most cases yes, you should suck it up and use them, even when they're demonstrably poor, because re-implementing them is a boatload of work and makes you incompatible with other people targeting the same chips.

 

But when the vendor is promoting their product as an ARM Cortex-M chip, but does not support the ARM standard interface designed for Cortex-M chips as other vendors do, this is IMO poor practice, and is enough to drive me to vendors that are more willing to cooperate within the ecosystem that they have chosen to join.

Share this post


Link to post
Share on other sites

@pabigot: thanks for the discussion on RMW and bit-banding. I will try to understand these terms.

 

Yes, I accept that we need to just use the driverlib since it is practical as a beginner

I only wanted to understand the architecture. 

 

Now, reading the driver lib user guide. It is giving good overview of the architecture. In parallel, I'm going into the Data sheet to know the specific registers and their settings.

 

Thanks

regards

Vin255

Share this post


Link to post
Share on other sites

 

    // Enable the GPIO pins for the LED (PF2).
#if CMSIS - 0
    GPIOF->DR2R |= GPIO_PIN_2;
    GPIOF->DEN |= GPIO_PIN_2;
    GPIOF->DIR |= GPIO_PIN_2;
#if 1
    { /* HardFault exception raised if there isn't a delay here */
      volatile uint16_t dd = 0;
      while (--dd);
    }
#endif
That last one is interesting; may be that driverlib's slowness obscures a bug (or may be that they're relying on it being slow to avoid having to put in some delay that I don't know about).

 

Turns out that, although CMSIS structure code I provide executed the same effective operations as TivaWare's function in the same order, it was wrong: section 10.3 of the TM4C123GH6PM data sheet specifies the configuration process, and has the GPIODIR register set first, not last. No idea why it works in the "wrong" order for TivaWare or with the delay, but if the GPIOF->DIR assignment is moved first the delay is not necessary.

Share this post


Link to post
Share on other sites

 

 

Bit-banding is relevant for operations on a GPIO DATA register, which is (in the CMSIS-based modification I used) declared as an array, and:

GPIOF->DATA[GPIO_PIN_2 | GPIO_PIN_6] = 0xFFdoes use bit-banding and only affects pins 2 and 6.

@pabigot:

Yes, you are right - I just peaked up the wrong example.

 

As for the operation "bad" order in Tiva, I have no idea - I always try to write in order...

Regards,

L

Share this post


Link to post
Share on other sites

Hello Lyon, pabigot and others

     I have one question regarding the bit banding for GPIODATA. I can turn on the GREEN LED using 2 approaches:

 

1. In the API GPIOPinWrite(GPIO_PORTF_BASE, LED_GREEN, LED_GREEN);

(Here LED_GREEN = 0x000000008 , Pin3 )

 

in the API definition the pins used are left shifted by 2 since in the Datasheet it is told that the address[9:2] acts as a mask to read/write values to GPIODATA

 HWREG(ulPort + (GPIO_O_DATA + (ucPins << 2))) = ucVal;

 

Here ulPort = GPIO_PORTF_BASE = 0x4002.5000 

                        GPIO_O_DATA =0, offset is 0

so at the address of 0x4002.5020 the value of 0x08 is written.

 

 

2. I can do the same using 

GPIO_PORTF_DATA_R |= 0x08;  // where GPIO_PORTF_DATA_R  = 0x4002.53FC as defined in lm4f120h5qr.h

Here all the bits of address line [9:2] are set as 1. Therefore whatever value on the right hand side, it is stored in the correct location.

 

My question here is the addresses are different in both the approaches but correct values goes into correct position! the green led is ON.

 

Can anyone please clarify this doubt for me.

Thanks a lot.

 

Regards

Vin255

Share this post


Link to post
Share on other sites

You've got things swapped around.

 

The address bits specify which bits of the value on the RHS are to be stored. Writing 0xa5 to 0x4002.53fc would turn on bits 0, 3, 5, and 7, and turn off bits 1, 2, 4, and 6.

 

Writing 0xff to 0x4002.5020 would set bit 3 (4U << 3) and not affect any other pins on the port.

 

The way I do it is something like:

GPIOF->DATA[1U << PIN] = -1; /* RHS could equivalently be (1U << PIN) since only PIN bit matters */
to turn on only PIN regardless of what value PIN is. In this case DATA is a 256-element uint32_t array that overlays the bitbanded memory.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...