Rickta59 589 Posted January 14, 2012 Share Posted January 14, 2012 [EDIT: This code concept has evolved into "fabooh" find it here on github https://github.com/RickKimball/msp430_code/tree/master/fabooh ] I've been experimenting with msp430-gcc uniarch trying out different approaches to implement a fast GPIO abstraction layer that hides some of the bitmask gymnastics we all seem to do when programming the msp430. I think I've come up with a reasonable approach and I'm hoping for some feedback from all of you. Normally to access the gpio ports on the msp430 with 'C', you use a bit ANDing (P1OUT & BIT4) and ORing ( P1OUT | BIT1 ) style of programming to read and write the bits. In the Arduino world, they hide all the bit magic using digitalRead and digitalWrite. The downside, at least on the Arduino, is those instructions aren't very efficient. However, reading code written for the Arduino API does seem somewhat more understandable than a bunch of ANDs and ORs. I want to make my code more readable, but I didn't want to sacrifice efficiency. After rejecting a few different methods, such as macros and functions with port tables, I came up with a C++ template that can be used to hide most of the bit mask programming and still manages to be reasonably efficient. Here is an example of toggling some leds on and off using a timer: /** * gpiotest.cpp - experimental c++ template based gpio abstraction * * author: rick@kimballsoftware.com * * 2012-01-14 - this version is about 168 bytes using msp430-gcc -Os */ #include #include #include "gpio.h" GPIO<0> LED1; // P1.0 GPIO<6> LED2; // P1.6 /** * millis - access to milliseconds since startup * * Note: because the value is stored in a 16 bit int, * it rolls over every 65536 milliseconds or 65.536 seconds */ static uint16_t millis; int main(void) { WDTCTL = WDT_MDLY_0_5; // Configure Watchdog as .5 second interval timer // use default ~1.024 MHz DCO clock digitalWrite(LED1,HIGH); digitalWrite(LED2,LOW); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); IE1 |= WDTIE; // Enable WDT interrupt while(1) { // Go to sleep until the Watchdog wakes us _BIS_SR(LPM0_bits + GIE); // The WDT_INTERVAL_ISR brings the CPU out of sleep mode // and we end up here, every 8 seconds or so if ( millis % 8192 == 0 ) { volatile int i=0; i=i; } } return 0; } /** * WDT_INTERVAL_ISR -Watchdog Timer ISR handler * * Note: assumes MCLK is 1.024MHz and we are using the * WatchDog Interval timer mode of WDT_MDLY_0_5 */ #ifdef __GNUC__ __attribute__( (interrupt (WDT_VECTOR)) ) #else #pragma vector = WDT_VECTOR __interrupt #endif void WDT_INTERVAL_ISR(void) { static uint8_t _500uSCount= 0; // count each .5mSec tick // every 2 ticks update the millis counter if ( (++_500uSCount % 2) == 0) { millis++; if ( millis % 64 == 0) { // toggle every 64 mSec, uses power of 2 value for smaller code LED1.toggle(); // our 2 leds are out of sync LED2.toggle(); // one is on when the other is off _BIC_SR_IRQ(LPM0_bits); // leave the ISR and exit sleep mode } } } To use the code you include "gpio.h" .. this declares the GPIO template and provides accessor functions for reading / writing and configuring the ports and pins. To use the launchpad leds, just create an instance of the GPIO object for each pin: GPIO<0> LED1; // P1.0 GPIO<6> LED2; // P1.6This gives you an object that allows you to manipulate the pin values. You can do things like LED1.setHigh() or LED1.setLow() to toggle that pin HIGH and LOW. LED1.setHigh() is doing a P1OUT |= BIT0; under the covers. LED1.setLow does a P1OUT &= ~BIT0. To use the pin as output you need to configure it. Normally you would do a P1SEL |= BIT0; to make it an output pin. With the gpio.h you can just use LED1.configureAsOutput(). As an example of how we can be more Arduino like, I provided a macro so you can also use "pinMode(LED1, OUTPUT)" instead. Because we are using c++ templates, the compiler is very efficient at optimizing the code. In the example, I use two 'C' statements to set the pin modes. However the compilers optimizes it down to a single msp430 instruction. pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); c04c: f2 d0 41 00 bis.b #65, &0x0022 ;#0x0041 c050: 22 00 In fact, the example code is only 168 bytes of msp430 object code. This seems very reasonable to me for readable C code. Using C++ templates with structures has another advantage over traditional C++ classes done Arduino style. if you don't use a function you don't incur any overhead. The compiler only generates object code for the functions you use. This allows you to create all the various helper methods you might want and not end up with giant code bloat. I've concentrated on making the experiment work with the msp430g2553 and msp430g2231. However it should be fine with any of the 'G' series of chips. You need to look at the way the GPIO structure is laid out and make sure it maps to the special memory areas for your chip. This isn't a finished work, it is just a starting point for those hoping for some GPIO abstraction that that still yields efficient code. Take a look at the gpio.h and the sample code and tell me what you think. It should compile with both CCS and msp430-gcc ( although I haven't tried CCS ) You can find the latest version of the code here: https://gist.github.com/1597239 It has the following files: gpiotest.cpp - is the sample program gpio.h - the GPIO template declaration [Edited 1/28/2012] updated code with more implementation methods -rick yyrkoon, Automate, oPossum and 4 others 7 Quote Link to post Share on other sites
pabigot 355 Posted January 14, 2012 Share Posted January 14, 2012 I've been experimenting with msp430-gcc uniarch trying out different approaches to implement a fast GPIO abstraction layer that hides some of the bitmask gymnastics we all seem to do when programming the msp430. I think I've come up with a reasonable approach and I'm hoping for some feedback from all of you. The only thing to watch out for is that the structures you're using don't have a volatile qualifier on the peripheral registers, which is why this happens: Because we are using c++ templates, the compiler is very efficient at optimizing the code. In the example, I use two 'C' statements to set the pin modes. However the compilers optimizes it down to a single msp430 instruction. pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); c04c: f2 d0 41 00 bis.b #65, &0x0022 ;#0x0041 c050: 22 00 Because they're not volatile, the compiler is free to optimize the bejeezus out of the code, possibly reordering and eliminating some assignments if it sees a subsequent assignment to the same variable with no intervening read. With some registers, putting both bit sets into a single operation would result in very wrong behavior. E.g., a sequence I wrote earlier today: UCB0CTL1 |= UCSWRST; UCB0CTL1 &= ~UCSWRST; might very well be completely eliminated, resulting in corrupted I2C reads. I suspect this could result in some very difficult to understand bugs. You can avoid the problem by adding volatile qualifiers in the structure declarations, though of course you'll lose the corresponding performance. However, the readability/simplicity would remain. oPossum, yyrkoon, Rickta59 and 1 other 4 Quote Link to post Share on other sites
Rickta59 589 Posted January 14, 2012 Author Share Posted January 14, 2012 Thanks Peter, this is just the kind of feedback I was looking for. What was I thinking? Of course those memory mapped registers need to be marked as volatile, the hardware is updating them and the value can change at any moment. I updated the gpio.h and changed the sample so that it performed some serial toggling of the same pin: ... LED1.setHigh(); LED1.setLow(); LED1.setHigh(); ... msp430-gcc seems to generate the proper code even with -Os turn on: inline void setHigh() _INLINE_ { reinterpret_cast(PORT_BASE_ADDR)->_OUT |= PIN_MASK(); c03e: d2 d3 21 00 bis.b #1, &0x0021 ;r3 As==01 } inline void setLow() _INLINE_ { reinterpret_cast(PORT_BASE_ADDR)->_OUT &= ~(PIN_MASK()); c042: f2 f0 fe ff and.b #-2, &0x0021 ;#0xfffe c046: 21 00 inline pin_value getValue() _INLINE_ { return ( reinterpret_cast(PORT_BASE_ADDR)->_OUT & PIN_MASK() ) ? HIGH : LOW; } inline void setHigh() _INLINE_ { reinterpret_cast(PORT_BASE_ADDR)->_OUT |= PIN_MASK(); c048: d2 d3 21 00 bis.b #1, &0x0021 ;r3 As==01 } inline void setLow() _INLINE_ { reinterpret_cast(PORT_BASE_ADDR)->_OUT &= ~(PIN_MASK()); c04c: f2 f0 bf ff and.b #-65, &0x0021 ;#0xffbf c050: 21 00 -rick RichardVowles 1 Quote Link to post Share on other sites
MattTheGeek 99 Posted January 28, 2012 Share Posted January 28, 2012 And my EasyMSP IO C library does this for the CCS compiler and yields extremely efficient code (No different than just doing the bitmask). Just putting a CCS option out there. (And hopefully with time a GCC option) I really want to work towards a "standard" C library for the MSP430, Any interests? Quote Link to post Share on other sites
Rickta59 589 Posted January 28, 2012 Author Share Posted January 28, 2012 I looked at EasyMSP before bothering with this code. Unfortunately the version I tried with msp430-gcc generated really bloated code. I could have modified the code to use macros to get the code size down, however then I would have lost the advantage of typesafe code checking I get for free with templates. However, I agree, it would be great to have a framework that works well with both msp430-gcc and CCS and isn't limited to 16k. There seems to be different approaches to makes it easier to write code for the msp430. If you live in the TI CCS world you can use GRACE to generate application specific code. This is a nice approach however it doesn't really help those who want to use gcc. Then there is the framework approach of MSP430Ware. Again nice if you are in the CCS world but it adds the additional requirement of a larger msp430F5xxx type device. It doesn't really do anything for people playing with the G series chips. Both of these approaches work if you are happy staying in CCS land and a 16k code limit. People migrating over from the Arduino world want to just be able to get a lower cost Arduino. For that group of people, they just want to make minimal changes to their sketches and have them work on the lower cost msp430 devices. Sometimes that works sometimes it doesn't. Arduinos generally assume a 1 or 2 clock cycle for GPIO toggling. Using an msp430 the best you can do is 4-5 cycles per GPIO toggle. However, you can write msp430 code that uses its peripherals and you might end up being faster than the Arduino. So making the migrating Arduino crowd happy isn't always going to end with a good result unless they put some effort in to learning the msp430. If you want to implement a "standard" library, you end up with many unanswered questions about what would be the best type of framework or code generator to build. I haven't got a general answer to that question. For my projects, I want a framework that works well with msp430-gcc with the G series of chips. Occasionally, I'm interested in working with larger msp430f5xxx chips. I want the framework to produce small fast code while still being readable. Unless CCS is released for free with unlimited code size, msp430-gcc is going to be the only free option that makes any sense. So you can see my motivation for experimenting with this style of coding that works in both msp430-gcc and CCS. I hope others find it useful. -rick turd 1 Quote Link to post Share on other sites
larsie 121 Posted January 31, 2012 Share Posted January 31, 2012 I like the intention behind both of these projects to simplify the IO. When I moved from Arduino to MSP430, it took me a while to get this into my fingers, and lowering the hurdle is good. Can it be done in such a way that the interface as seen from the developer is the same for Rickta59's and EasyMSP's libraries? So that the function calls are the same, but we just use different header files? Personally my interest is in MSPGCC and the value-line series, as I'd like to use something like this within the Inventortown IDE, and also would love to be able to point to some good tutorials on doing simple IO. My long term ambition is to make a graphical representation of it, though that's still in my dreams ... I'd love to get good libraries for all the other ones also, such as servo, serial, reading the length of pulse-in, shifting pulses out or in a pin (shiftout), i2c, spi and timers also. Maybe also something to simplify interrupts. And something for configuring the clock in a simpler way (I still find all the different options confusing). I have some servo-implementations, i2c etc on my site, but they are quite basic and need some work to be improved and generalized. Some of this is in EasyMSP, but I'm focussing on MSPGCC. But if we at least had some common interfaces across the different IDEs/compilers, that would help. Quote Link to post Share on other sites
yyrkoon 250 Posted January 15, 2013 Share Posted January 15, 2013 Just compiled the example on Energia, dumping the rest of the framework just using main.cpp, and this example compiles in as little as: Binary sketch size: 211 bytes (of a 16,384 byte maximum) Very impressive, good job Rick. I will be putting this to use myself. Just for comparison. A "bare" main.cpp compiles to 107 bytes. Quote Link to post Share on other sites
Rickta59 589 Posted January 15, 2013 Author Share Posted January 15, 2013 Now that I've played with this scheme for a while I'm not really happy with it. Off and on I've played with a better implementation. I might have something better in the near future. -rick Quote Link to post Share on other sites
yyrkoon 250 Posted January 15, 2013 Share Posted January 15, 2013 Awesome, although I would be perfectly happy with gpio.h as it stands. For sure, I have not used it as extensively as you have though. Quote Link to post Share on other sites
brainwash 12 Posted March 4, 2013 Share Posted March 4, 2013 Sorry to be such a noob, but I don't know how to get this to work: "C:/ti/ccsv5/tools/compiler/msp430_4.1.4/bin/cl430" -vmsp --abi=eabi -g --include_path="C:/ti/ccsv5/ccs_base/msp430/include" --include_path="C:/ti/ccsv5/tools/compiler/msp430_4.1.4/include" --advice:power=all --define=__MSP430G2553__ --diag_warning=225 --display_error_number --diag_wrap=off --printf_support=minimal --preproc_with_compile --preproc_dependency="blink.pp" "../blink.c" "..\gpio.h", line 80: error #78-D: this declaration has no storage class or type specifier "..\gpio.h", line 80: error #66: expected a ";" Line 80: template<uint8_t PINNUM> struct PORTINFO { static int const BASE_ADDR = PINNUM < 8 ? P1BASEADDR My code: #include <msp430.h> #include <stdint.h> #include "gpio.h" ........ GPIO<5> ASSY_MISPLACED_SW; Quote Link to post Share on other sites
Rickta59 589 Posted March 4, 2013 Author Share Posted March 4, 2013 needs to be in a .cpp file is it? To be honest, I've left this approach behind and gone with a new approach. Still very much in an alpha state though: https://github.com/RickKimball/msp430_code/tree/master/fabooh I've also stopped trying to make it work with CCS. You will have to do that yourself. -rick Quote Link to post Share on other sites
brainwash 12 Posted March 5, 2013 Share Posted March 5, 2013 Thank you. I didn't know it was supposed to be in a .cpp file. Now at least it compiles so if I find any runtime problems with CCS I will report them here. I only know Java well and a tiny bit of C, so this is a bit steep to me, but it's a much better abstraction than having to bit juggling, even if you manage abstract a lot of the math. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.