Jump to content
Sign in to follow this  
nixnerd3

Energia Hybrid Code

Recommended Posts

Hi all,

I started writing a few "hybrid" code  examples based on Energia projects. They're called hybrid because they're based on Energia examples but are written more of a compact  form, using few header files.  For example, they do not try to hide the main() routine like Arduino/Energia (this is great for beginners/prototypers, but not for understanding the microprocessor).  Energia and CCS examples are somewhat complementary: Energia is easy to use/hard to follow, TI code is hard to use/easy to follow. Hi-level vs. low-level. So I wanted something in between. Hence, hybrid.

They are written with the intent to expose the underlying details, not to try to hide them like Arduino/Energia paradigm. The intent is to make them self-contained without too many header files. One significant departure is to specify ports in the TI style of P1.x instead of mapping them to single numbers (P1.0 = 1, etc) . Not sure if that's a bad idea.

Here is a rough draft of Blinky:

#include <msp430.h>

typedef unsigned char uint8_t;
typedef enum _val {LOW, HIGH} _val_t;
typedef enum _dir {INPUT, OUTPUT} _dir_t;

void disableWatchDog()
{
        WDTCTL = WDTPW | WDTHOLD;
}

void init()
{
        disableWatchDog();
}


void pinMode(uint8_t port, uint8_t pin, _dir_t dir){
        volatile unsigned char* _base_addr = &P1DIR; //base starting address
        volatile unsigned char* _port_addr;          //final calculated addr
        uint8_t offset; // port address equals base plus offset
        offset = (port - 1) << (port + 1); // each port seperated by 8 bytes, hence shift a 1 by 2^3
        _port_addr = _base_addr + offset;
        uint8_t mask = 1<<pin;
        (dir == INPUT) ?  (*_port_addr &= ~mask) : (*_port_addr |= mask); //set or reset bit
}


void setup() {
  uint8_t port = 1; //Port 1.0
  uint8_t pin =0;
  pinMode(port, pin, OUTPUT);
}


void digitalWrite(uint8_t port, uint8_t pin, _val_t val)
{
        volatile unsigned char* _base_addr = &P1OUT;
        volatile unsigned char* _port_addr;          //final calculated addr
        uint8_t offset; // port address equals base plus offset
        offset = (port - 1) << (port + 1); // each port seperated by 8 bytes, hence shift a 1 by 2^3
        uint8_t mask = 1<<pin;
        _port_addr = _base_addr + offset;
        (val == LOW) ?  (*_port_addr &= ~mask) : (*_port_addr |= mask); //set or reset bit
}

// the loop routine runs over and over again forever:
void loop() {
  uint8_t port = 1; //Port 1.0
  uint8_t pin =0;
  digitalWrite(port, pin, HIGH);   // turn the LED on (HIGH is the voltage level)
  __delay_cycles(50000);               // wait for a second
  digitalWrite(port, pin, LOW);    // turn the LED off by making the voltage LOW
  __delay_cycles(50000);               // wait for a second
}

int main(void)
{
    init();
    setup();
    while(1) loop();
}

Comments are encouraged! is this useful/useless, redundant, confusing, inaccurate, inconsistent, awesome/irrelevant, helpful, etc?

thank you,

Scott

Share this post


Link to post
Share on other sites

I completed the MOOC Embedded Systems: Shape The World — MOOC edX UTAustinX UT.6.02x and learnt how to define an analog input "the real way".

  • Real way
// Declare analog input PE2/A1
SYSCTL_RCGC2_R |= 0x00000010;
GPIO_PORTE_DIR_R &= ~0b00000100;
GPIO_PORTE_AFSEL_R |= 0b00000100;
GPIO_PORTE_DEN_R &= ~ 0b00000100;
GPIO_PORTE_AMSEL_R |= 0b00000100;
SYSCTL_RCGC0_R |= 0x00010000;
SYSCTL_RCGC0_R &= ~0x00000300;
ADC0_SSPRI_R = 0x0123;
ADC0_ACTSS_R &= ~0x0008;
ADC0_EMUX_R &= ~0xf000; 
ADC0_SSMUX3_R &= ~0x000f; 
ADC0_SSMUX3_R += 1; 
ADC0_SSCTL3_R = 0x0006; 
ADC0_ACTSS_R |= 0x0008; 

// Read value form PE2/A1
ADC0_PSSI_R = 0x0008; 
while ((ADC0_RIS_R & 0x08) == 0);
result = ADC0_SSFIFO3_R & 0xfff;
ADC0_ISC_R = 0x0008;
  • Energia way
pinMode(A1, INPUT);
uint16_t value = analogRead(A1);

It is interesting the first time, but laziness made me use the Energia way the times after. Remember laziness is how developers survive :).

Share this post


Link to post
Share on other sites

Another approach with a simplified API is drverlib which is provided by TI and the use of #define.   For example, to blink a LED:

#include "driverlib.h"

#define  RED_LED       GPIO_PORT_P1,GPIO_PIN0

#define  DELAY_CYCLES  104500

//--------------------------------  m a i n  -----------------------------------
void main(void)
{
	// setup
    WDT_A_hold(WDT_A_BASE);                  // stop watchdog timer

    GPIO_setAsOutputPin(RED_LED);            // set red LED port and pin to output direction

    // loop
    for(;;){

    	GPIO_toggleOutputOnPin(RED_LED);     // toggle the red LED

    	_delay_cycles(DELAY_CYCLES);         // delay a while
    }
}

 

Share this post


Link to post
Share on other sites
4 hours ago, Rei Vilo said:

It is interesting the first time, but laziness made me use the Energia way the times after. Remember laziness is how developers survive :).

Agreed. I have no idea of the hundreds of hours I saved by using published code snippets in pursuit of both deadlines and laziness.  Interesting that in virtually any other field the "copy/paste" approach could be plagiarism...

Laziness vs real way vs driverlib/Energia:
I often adopt "lazy" for initial prototyping - get it to work!
Then run in a debugger to see what's happening "under the covers." That allows for some analysis regarding efficiency - e.g. battery life (most of my work is not timing dependent, so battery life matters most).
I started coding on '430 devices using the TI code examples, and still lean towards the "real way" since it helps me see/understand what's happening, when and why.

An Aside Re: TI code examples and Launchpad  code - There seems to have been little communication between the various developers.  When it come to comms, be it I2C, Serial, USB, working TI examples seem to ALWAYS use different pin/port combinations and ISR handlers.  Once I realized that, I found that search/replace usually allowed porting code example to the launchpads. (FWIW - when it dawned on me that there are two I2C handlers on the F5529, I realized I could code a "loopback" version of their I2C Master/Slave examples in one source code file with a pair of patch wires and a pair of 10K resistors.  Simplifies debugging test code... Next step, add interrupt-driven serial output to those simple I2C loopback routines for the times I don't want to use a logic analyze or scope.)

Ok, I've digressed.  I find Energia useful for simple proof-of-concept prototypes, and useful after importing to CCS for step-by-step debugging (Note to self: Don't forget to disable serial output when debugging!!!).  I've mostly abandoned the driverlib approach; honestly, I find it way too obscure AND in most instances, it avoids lowpower modes, using "while true" loops which can play havoc with both power management as well as handling communication problems - the apps tend to hang in a forever loop...
 

BTW - great link to the MOOC - something to explore in the coming months. I can always use more structure...

Share this post


Link to post
Share on other sites

All,

Thanks for raining on my parade :) Seriously, though, thank you for your input. One thing I should've mentioned is this is for pedagogical purposes, not for development. For example, it might make a good demo for MOOC to show students a level up from the "real" code, but not requiring a lot of skill with using libraries, include paths, linking, etc.

Probably the easiest thing for me to do is get to know the Energia library better.

Other than that, maybe you can help me figure out an easier way to debug.The Energia code is very robust, easy to use, yes, but it can take some time for new user to make their way thru the labyrinth of references.

Just to give you a concrete example, consider this line in wiring_analog.c:

uint16_t analogRead(uint8_t pin)
{
// make sure we have an ADC
	uint8_t channel;
	
	// Check if pin is a special analog pin (A10 = temp sensor, A11 = Vcc/2, etc.)
	if (pin >=128)
		channel = pin - 128;
	else
		channel = digitalPinToADCIn(pin);

 

The question becomes, where is digitalPinToADC() defined? Well, after poking around a bit, I find it's a #define  (what would I do without grep?) in:

grep digitalPinToADCIn hardware/energia/cores/msp430/*

Energia.h:#define digitalPinToADCIn(P)      ( digital_pin_to_analog_in[P] )

So, searching around a bit more, I find digital_pin_to_analog_in[] is an array declared in 

hardware/energia/msp430/variants/MSP-EXP430G2553LP/pins_enerigia.h

const uint32_t digital_pin_to_analog_in[] = {
        NOT_ON_ADC,     /*  dummy   */
        NOT_ON_ADC,     /*  1 - 3.3V*/
        0,            /*  2 - A0 */
        1,              /*  3 - A1 */
        2,             /*  4 - A2 */
        3,             /*  5 - A3 */
        4,             /*  6 - A4 */
        5,            /*  7 - A5 */
        NOT_ON_ADC,    /*  8 - P2.0 */
        NOT_ON_ADC,    /*  9 - P2.1 */
        NOT_ON_ADC,    /*  10 - P2.2 */
        NOT_ON_ADC,    /*  11 - P2.3 */
        NOT_ON_ADC,    /*  12 - P2.4 */
        NOT_ON_ADC,    /*  13 - P2.5 */
        6,              /*  14 - A6 */
        7,              /*  15 - A7 */
        NOT_ON_ADC,    /*  16 - RST */
        NOT_ON_ADC,     /*  17 - PF0 */
        NOT_ON_ADC,    /*  18 - PE0 */
        NOT_ON_ADC,     /*  19 - PB2 */
        NOT_ON_ADC     /*  20 - GND */
};

The gripe I have with this is that it's hard to debug using #defines. It's easier with const, of courseCode Composer has no idea what digitalPinToADC() is, but it will tell you what digital_pin_to_analog_in is. You and I don't have a problem with this (although we might be somewhat bemused), students will stumble. 

 I think what I'd like to see is just one example (say, just for the 2553 Launchpad) written in a bare-bones, flat, not much error-checking, but still using a structured C++ style like Energia code for some of the Energia examples. 

Does that make sense?

thank again!

Share this post


Link to post
Share on other sites

First, an in-depth answer to your request is above my pay grade.  That said...

Energia (Arduino) targets an audience of non-programmers, as you're well aware. Students have fundamentally different needs & requirements.

Further, regarding OO, a couple of decades back I overheard this brief exchange between a junior and senior engineer: 

Question - I thought OO was supposed to make everything transparent?

Answer - Transparent to the user, opaque to the programmer.

OO is a complex world. So is Nursing, which is what I teach.  There are parallels in that both require a substantial level of "background" information and understanding.  In Nursing, it's a basis in biology, physiology, chemistry, biochemistry, pathophysiology, bacteriology, psychology, anatomy, and more. With OO, there are the basic concepts of "Is" and "Has," which have a "fractal" relationship to their final implementations, the deeper you go, the deeper you can go... I find one of the main challenges with OO design is figuring out where to stop diving down.

So, how to accomplish your goal? getting students (I assume at the very early stages of entry level) on board with enough background and intrinsic knowledge so that they can start moving forward exploring the more creative aspects of software applications and implementations.

I suspect that your choices are:

1) Write your own example code.  Time consuming, to say the least. 

2) Use the existing tools and code, which entail their own levels of complexity and obfuscation.
For my own edification I have pursued the second route, followed by attempting the first as I try to implement what I THINK I may have learned.

So, for example:

1) Start with an Energia implementation of Blink the LED.

2) Import the Energia project in to CCS, which will allow stepping through the code at varying levels of detail, first the loop(), then start looking at how the device is configured and the LED toggled.

3) Use the TI Blink example C code for the device to see a raw C implementation based on delay cycles. (This is also easily "ported" to an IAR kickstart version).

4) Look at an interrupt-based Blink application, introduces timers, ISRs and event-driven programming.  (Either CCS or IAR).

5) For a not-really-OO implementation, use the driverlib from TI.

I doubt the above accomplishes what you want, but an old adage applies here: If it was easy, anyone could do it...

Best,

Bob

Share this post


Link to post
Share on other sites

Hi Bob,

Again, thank you very much for your input. Hmm...I'll probably do a little of each step  you've outlined. And I'll keep the mantra of "If it was easy anyone could do it" in mind.

all, again, thank you for your help!

Scott

Share this post


Link to post
Share on other sites

BTW, I didn't intend to imply that OO  design and coding require a less demanding level  intrinsic/background knowledge.  Having worked in that world, I KNOW it's easily as complex as medical fields. :)

Share this post


Link to post
Share on other sites

For production, I'm using Energia for rapid prototyping. If some parts need to go faster, then I use the DriverLib on ROM, and eventually the registers for further optimisation (I get useful help from this very forum on how to use HWREG). However, the more optimised the code is for speed , the less portable the code is across MCUs. 

As a matter of facts, most prototypes are "good enough" even for final product. The killer feature of C++ (and thus Energia) is OOP, making maintenance really easy. That's how I've built the LCD_screen Library Suite for the screens supported by the LaunchPads. Adding a new screen only requires defining a couple of functions.

Now, there is no secret: different programming methods just move the difficulty from one part to another. It is always interesting to compare different solutions, so students can weight the pros and the cons of each of them (e.g. One Example, Three Environments), and discover that previous analysis is key, whatever the solution.

I did the same with RTOS: I completed the Real-Time Bluetooth Networks: Shape the World — MOOC edX UTAustinX UT.RTBN.12.01x MOOC and built the kernel of an RTOS from scratch. Now, I'm very happy to use TI-RTOS or Energia MT directly, with the ease of RTOS elements packaged as objects with the Galaxia library for Energia MT.

Share this post


Link to post
Share on other sites

Rei,

You are clearly demonstrating the benefits of a deeper level of knowledge, which allows you to pick your battles (so to speak), and focus on the production of both code and devices that are efficient not only when they run, but also during the development process.

Thanks for sharing your insights.

Bob

Share this post


Link to post
Share on other sites
Quote

Thanks for raining on my parade :) Seriously, though, thank you for your input. One thing I should've mentioned is this is for pedagogical purposes, not for development.

I think what you are doing is fine for pedagogical purposes and for that matter development.  I too have been frustrated at times by not knowing what is going on in Energia and the time it takes to hunt it down.  Nonetheless, I found it useful especially when starting out.  Note that when you use DriverLib it is possible to hover the cursor in CCS and see what is going on underneath.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×