Shop OBEX P1 Docs P2 Docs Learn Events
Please comment on my code. I'm I "getting" it ? — Parallax Forums

Please comment on my code. I'm I "getting" it ?

BeanBean Posts: 8,129
edited 2012-06-22 14:24 in Propeller 1
Here is a small library of code I'm working on.
I think I've gotten my head around this C stuff, but I'd like you comments.

Library "stuff.c"
/* 
   high(value)        - Makes pin an output and high
   low(value)         - Makes pin an output and low
   input(value)       - Makes pin an input
   output(value)      - Makes pin an output
   pausems(value)     - Pause for "value" milliseconds 
   serinit(pin, baud) - Setup serial parameters
   serout(byte)       - Send a byte to serial pin
   serstr(string)     - Send a string to serial pin
   serint(value)      - Send an integer converted to ascii to the serial pin
*/

#ifndef _PROPELLER_H_
#include <propeller.h>
#endif

#ifndef _STUFF_C_
#define _STUFF_C_

long int _serPinMask = 1 << 30;
long int _serBaudCnt = 19200;


void high(int given) {
  given = 1 << given;  // Create bit mask
  DIRA |= given;       // Make pin an output
  OUTA |= given;       // Make pin high
}

void low(int given) {
  given = 1 << given;  // Create bit mask
  DIRA |= given;       // Make pin an output
  OUTA &= ~given;      // Make pin low
}

void input(int given) {
  DIRA &= ~(1 << given);  // Make pin an input
}

void output(int given) {
  DIRA |= 1 << given;  // Make pin an output
}

void pausems(long int given) {
  if (given > 0) waitcnt(CNT + (CLKFREQ / 1000) * given);  // Wait "given" milliseconds
}

void serinit(int g_serPin, long int g_serBaud) {
  _serPinMask = 1 << g_serPin;        // Remember pin mask
  _serBaudCnt = CLKFREQ / g_serBaud;  // Remember baud rate
  DIRA |= _serPinMask;                // Make pin an output
  OUTA |= _serPinMask;                // Make pin high
}  

void serout(char given) {
  // Serial LCD character output
  int temp = (given << 1) + 512; // bit 0=start bit(0), bit9=stop bit(1)
  long int tempcnt;
  tempcnt = CNT + _serBaudCnt;  // Setup for waitcnt
  int i;
  for (i=0; i < 10; i++) {  // Sending 10 bits (start, 8-data, stop)
    if ((temp & 1) == 1) {  // Send bit in bit0 position
      OUTA |= _serPinMask;
    } else {
      OUTA &= ~_serPinMask;
    } // if
    tempcnt = waitcnt2(tempcnt, _serBaudCnt);  // Bit delay
    temp >>= 1;  // Shift to get next bit in bit0 position
  } // for i
}  // serout

void serstr(const char *given) {
  while (*given != 0) serout(*given++);  // Send characters until zero
}  // serstr

void serint(long int given) {
  if (given < 0) {  // Check if negative value
    given = -given;  // Use abs if negative
    serout('-');  // Emit a "-" if negative
  }
  int sum = 0;  // Sum of digits (used to emit leading spaces)
  char temp = '0';  // current digit
  // Must do highest digit as a special case because it can only be 0-4
  while (given >= 1000000000) {
    given -= 1000000000;
    temp++;
    sum++;
  }
  if (sum > 0) serout(temp);  // If is's not zero, emit it
  // Do rest of digits the same way
  int i;
  for (i = 0; i < 9; i++) {
    char temp = '0';
    while (given >= 100000000) {
      given -= 100000000;
      temp++;
      sum++;
    }
    if ((sum > 0) || (i == 8)) serout(temp);  // If last digit(i==8) always emit
    given *= 10;
  } // for i
} // serint

#endif // ifndef _STUFF_C_


Main code for SimpleIDE "test.c"
#include "stuff.c"

void main(void)
{
  pausems(1000);

  serinit(30, 115200);  // Setup for SimpleIDE terminal
  pausems(10);  // Give pin time to settle

  serstr("Here we go...\n");
  pausems(1000);

  int i;
  for (i=0; i < 100; i++) {
    serint(i);
    serout(10);
  } // for i

  serstr("All done...\n");

} // main


Thanks,
Bean

Comments

  • BeanBean Posts: 8,129
    edited 2012-06-22 06:15
    Another question...
    When I view the assembly for the test.c code it contains functions that are never called.
    I though the compiler would strip those out ?
    Is there any way to make it strip them out ?

    Bean
  • David BetzDavid Betz Posts: 14,516
    edited 2012-06-22 06:36
    Bean wrote: »
    Another question...
    When I view the assembly for the test.c code it contains functions that are never called.
    I though the compiler would strip those out ?
    Is there any way to make it strip them out ?

    Bean
    This is one reason to make a binary library (libbean.a for instance) containing each of your library functions in a separate file. That way the linker will only include the functions that are actually used by your main program.
  • BeanBean Posts: 8,129
    edited 2012-06-22 06:47
    David,
    I did some searching and found that if I prefix the function with "static" then they do get stripped out like I wanted.

    Bean
  • David BetzDavid Betz Posts: 14,516
    edited 2012-06-22 06:52
    Bean wrote: »
    David,
    I did some searching and found that if I prefix the function with "static" then they do get stripped out like I wanted.

    Bean
    Yes, that is probably true. It's because "static" tells the compiler that the function should only be visible to the file in which it is contained. That lets GCC notice that no one calls it and so it gets eliminated. That scheme won't work with global functions though so if your program ever expands to more than a single file you'll be back to the same problem. Either that, or you'll end up with multiple copies of any function that is used in more than one of your source files. You might want to spend a little time learning to make a library. That's the normal way to do this sort of thing in C.

    Anyway, I'm glad to hear you're making so much progress learning GCC!
  • jazzedjazzed Posts: 11,803
    edited 2012-06-22 08:00
    Hi Bean.

    I would probably structure the project a little differently.
    That is add stuff.c to the project rather than including it in test.c.
    Also by doing that you don't need to add the #ifdef ... #define ... #endif block in stuff.c
    The propeller.h include already has a #ifdef ... #define ... #endif block.

    Here's an improvement on serout() ... What you have is very close.
    The difference is placement of the tempcnt bit delay calculation.
    This one works with the 115200 terminal.
    void serout(char given) {
      // Serial LCD character output
      int i;
      int temp = (given << 1) + 512; // bit 0=start bit(0), bit9=stop bit(1)
      long int tempcnt;
      tempcnt = CNT + _serBaudCnt;  // Setup for waitcnt
      for (i=0; i < 10; i++) {  // Sending 10 bits (start, 8-data, stop)
        tempcnt = waitcnt2(tempcnt, _serBaudCnt);  // Bit delay
        if ((temp & 1) == 1) {  // Send bit in bit0 position
          OUTA |= _serPinMask;
        } else {
          OUTA &= ~_serPinMask;
        } // if
        temp >>= 1;  // Shift to get next bit in bit0 position
      } // for i
    }  // serout
    

    I changed your test.c slightly to get this output.
    Here we go...
    1 2 3 4 5 6 7 8 9 10 
    11 12 13 14 15 16 17 18 19 20 
    21 22 23 24 25 26 27 28 29 30 
    31 32 33 34 35 36 37 38 39 40 
    41 42 43 44 45 46 47 48 49 50 
    51 52 53 54 55 56 57 58 59 60 
    61 62 63 64 65 66 67 68 69 70 
    71 72 73 74 75 76 77 78 79 80 
    81 82 83 84 85 86 87 88 89 90 
    91 92 93 94 95 96 97 98 99 100 
    All done...
    
    void main(void)
    {
      pausems(1000);
    
      serinit(30, 115200);  // Setup for SimpleIDE terminal
      pausems(10);  // Give pin time to settle
    
      serstr("Here we go...\n");
      pausems(1000);
    
      int i;
      for (i=1; i <= 100; i++) {
        serint(i);
        serout(' ');
        if((i % 10) == 0)
            serout('\n');
      } // for i
    
      serstr("All done...\n");
    
    } // main
    
  • pedwardpedward Posts: 1,642
    edited 2012-06-22 14:24
    Perhaps this is a good time to mention that the PropGCC project isn't just a compiler, it's also a mini-kernel that supports some peripherals and memory devices.

    With that, there are implementations of Printf, simple serial (inline serial like above) and full duplex serial (cog based), as well as DOS FAT filesystem support

    I'm mentioning this because I am hoping that we can encourage more code reuse than in the past. It seems that OBEX is incredibly fragmented and has a lot of redundant code because of the fragmentation and lack of moderation or stewardship.

    I love to see Bean contributing. My first Propeller project was to port the Arduino runtime to SPIN so Arduinites could have a familiar set of functions. It's funny because much of that same high level code is being rewritten here.

    I *might* suggest starting with the Arduino SDK and porting their code to run on the Propeller with PGCC, it is license compatible and would save a lot of debugging effort. Obviously there are some significant architectural differences between the AVR and Propeller, but much of the SDK is agnostic and does things /the hard way/,in software :)

    Oh, another thing to point out, with GCC, you can take advantage of a lot of optimizations that SPIN can't do. When implementing many of the pin/bit functions, you layer function calls for code compactness, but in SPIN you lose performance. In GCC you can gain that performance back by inlining functions, so the multiple stack frames get smashed, effectively reiterating the same code, but with the high level source management.
Sign In or Register to comment.