Convert an ASCII String to Fixed Point: atofp()

conversion

Here is a small utility routine which converts an ASCII string floating point number into an s16.15 format fixed point number. Most fixed point libraries neglect this conversion. However, in practice, this routine is very useful. If the conversion process is not efficient, the gains of using fixed point over floating point math can be eliminated. Having said that, this is not pretty code and neither is it efficient. And it breaks a few coding rules too.

It is also important to note, the routine does very little (almost no) validity testing of the input values (size of integer/fixed point numbers, valid characters, sufficient string space, etc.). So there is plenty of opportunity here for spectacular failure.

The complementary conversion, fptoa() is also included.

//atol function ignores sign
int32_t _atol(const char* s) {
  int32_t v=0;
  
  while (*s == ' ' || (uint16_t)(*s - 9) < 5u) {
    ++s;
  }
  if (*s == '-' || *s == '+') {
    ++s;
  }
  while ((uint16_t)(*s - '0') < 10u) {
    v = v*10 + *s - '0';
    ++s;
  }
  return v;
}

#define MAX_STRING_SIZE 8

//basic string copy
static inline void _strcpy(char *d, const char *s) {
  uint8_t n=0;
  
  while (*s != '\0') {
    if (n++ >= MAX_STRING_SIZE) {
      //destination max size
      return;
    }
    *d++ = *s++;
  }
}

//basic string concatenation
void _concat(char *d, char *s) {
  uint8_t n=0;
  
  while(*d) {
    d++;
  }
  while(*s && n<MAX_STRING_SIZE) {
    *d++ = *s++;
    n++;
  }
  *d = '\0';
}

//int32_t atofp(char *)
int32_t FP_StrToFix(char *s) {
  int32_t f, fpw, fpf, bit, r[15] = {
    0x2faf080, 0x17d7840, 0xbebc20, 0x5f5e10, 0x02faf08, 0x017d784, 0x0bebc2, 0x05f5e1,
    0x002faf1, 0x0017d78, 0x00bebc, 0x005f5e, 0x0002faf, 0x00017d8, 0x000bec //0x0005f6
  };
  uint8_t sign, i;
  char *p=s, temp[9] = "00000000";

  sign = 0;
  //separate whole & fraction portions
  while (*p != '.') {
    //check for negative sign
    if (*p == '-') {
      sign = 1;
    }
    if (*p == '\0') {
      //no decimal found, return integer as fixed point
      return sign ? -(_atol(s)<<FP_FBITS) : (_atol(s)<<FP_FBITS);
    }
    p++;
  }

  //whole part
  *p = '\0';
  fpw = (_atol(s)<<FP_FBITS);

  //pad fraction part with trailing zeros
  _strcpy(temp, (p + 1));
  //get fraction
  f = _atol(temp);
  //re-insert decimal point
  *p = '.';

  fpf = 0;
  bit = 0x4000;
  //convert base10 fraction to fixed point base2
  for (i=0; i<15; i++) {
    if (f - r[i] > 0) {
      f -= r[i];
      fpf += bit;
    }
    bit >>= 1;
  }

  //join fixed point whole and fractional parts
  return sign ? -(fpw + fpf) : (fpw + fpf);
}

//void fptoa(int32_t, char *)
void FP_FixToStr(int32_t f, char *s) {
  int32_t fp, bit=0x4000, r[16] = { 50000, 25000, 12500, 6250, 3125, 1563, 781, 391, 195, 98, 49, 24, 12, 6, 3 };
  int32_t d[5] = { 10000, 1000, 100, 10 };
  char *p=s, *sf, temp[12];
  uint8_t i;
  
  //get whole part
  fp = ktoi(f);
  if (fp == 0) {
    *p = '0';
    } else {
      p = ltoa(fp, s, 10);
  }

  //get fractional part
  fp = FP_FRAC_PART(f);
  if (fp == 0) {
    return;
  }
  //iterate to end of string
  while (*p != '\0') p++;
  *p++ = '.'; //add decimal to end of s
  *p = '\0';  //terminate string
  
  f = 0;
  //convert fraction base 2 to base 10
  for (i=0; i<15; i++) {
    if (fp & bit) {
      f += r[i];
    }
    bit >>= 1;
  }
  //temporary string storage space
  sf = temp;
  sf = ltoa(f, sf, 10);
  
  // if needed, add leading zeros to fractional portion
  for (i=0; i<4; i++) {
    if (f < d[i]) {
      *p++ = '0';
      *p = '\0';
    } else {
      break;
    }
  }
  
  //combine whole & fractional parts
  _concat(s, sf);
}
Posted in Uncategorized | Tagged , , , , | 2 Comments

Arduino s16.15 Fixed Point Math Routines

It is important to note, that fixed point math comes in many flavors. For example, a 16-bit integer can implement 31 different fixed point formats (signed and unsigned Q1 through Q16). A couple of popular 16-bit formats being Q8.8, Q16 and s15. Each version has distinct ranges for the numbers the format can represent. Additionally, each type will have significant differences in the precision that can be achieved. So, when one presents fixed-point functions or a library, there is a good chance the implementation is unique and specific to the programmer’s task. It’s highly doubtful you’ll find a single-solution that fits all purposes. Having said all that, here are some fixed point functions for the arduino that I cobbled together.

Most of this was copied from the source code of avrfix, fixedptc and the AVR GCC library sources. Very little of this is new, I simply massaged a few bytes here and there and combined it all together. I changed a few function names, but left enough hints inside the assembler routines if you want to discover where they came from.

This should provide very rudimentary fixed point math, allowing a base to build upon. It uses a signed long (int32_t) in the form of a s16.15 fixed point value. There is no support for overflow/saturation. I’ve added basic square root and trig functions.

I hope I left all of the necessary attributions in the file. I’ve conducted very minimal testing, and you need to determine if the accuracy fits your needs. Use at your own risk!

fix.h:

#ifndef FIX_H
#define FIX_H

//Pragmas
#define FP_IBITS       16 //integer bits
#define FP_FBITS       15 //fraction bits
#define FP_BITS        32 //total bits (s16.15)
#define FP_MIN         -2147450880L
#define FP_MAX         2147450880L
#define FP_FMASK       (((int32_t)1<<FP_FBITS) - 1)
#define FP_ONE         ((int32_t)0x8000)
#define FP_CONST(R)   ((int32_t)((R)*FP_ONE + ((R) >= 0 ? 0.5 : -0.5)))
#define FP_PI          FP_CONST(3.14159265358979323846)
#define FP_TWO_PI      FP_CONST(2*3.14159265358979323846)
#define FP_HALF_PI     FP_CONST(3.14159265358979323846/2)
#define FP_ABS(A)      ((A) < 0 ? -(A) : (A))
#define FP_FRAC_PART(A) ((int32_t)(A)&FP_FMASK)
#define FP_DegToRad(D) (FP_Division(D, (int32_t)1877468))
#define FP_RadToDeg(R) (FP_Multiply(R, (int32_t)18529868))

//basic math
extern int32_t FP_Multiply(int32_t, int32_t);
extern int32_t FP_Division(int32_t, int32_t);

//special functions 
int32_t FP_Round(int32_t, uint8_t);

//conversion Functions
extern float FP_FixedToFloat(int32_t);
extern int32_t FP_FloatToFixed(float);
#define itok(i)        ((int32_t)((int32_t)i<<(int32_t)15))
#define ktoi(k)        ((int16_t)((int32_t)k>>(int32_t)15))
#define ftok(f)        ((int32_t)(float)((f)*(32768)))

//square root
extern int32_t _FP_SquareRoot(int32_t, int32_t);
#define FP_Sqrt(a)     _FP_SquareRoot(a, 15);

//trig
extern int32_t FP_Sin(int32_t);
#define FP_Cos(A)      (FP_Sin(FP_HALF_PI - A))
#define FP_Tan(A)      (FP_Division(FP_Sin(A), FP_Cos(A)))

#endif //FIX_H 

fix.c:

/*
 * The ideas and algorithms have been cherry-picked from a large number
 * of previous implementations available on the Internet, and from the
 * AVR GCC lib sources.
 * Copyright (c) 2002  Michael Stumpf  <mistumpf@de.pepperl-fuchs.com>
 * Copyright (c) 2006  Dmitry Xmelkov
 * Copyright (C) 2012-2015 Free Software Foundation, Inc.
 * Contributed by Sean D'Epagnier  (sean@depagnier.com)
 * Georg-Johann Lay (avr@gjlay.de)
 * All rights reserved.
 * Maximilan Rosenblattl, Andreas Wolf 2007-02-07
 * Copyright (c) 2010-2012 Ivan Voras <ivoras@freebsd.org>
 * Copyright (c) 2012 Tim Hartrick <tim@edgecast.com>
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions and the following disclaimer in
 the documentation and/or other materials provided with the
 distribution.
 * Neither the name of the copyright holders nor the names of
 contributors may be used to endorse or promote products derived
 from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/
#include <avr/io.h>
#include "fix.h"

//__mulsa3
int32_t __attribute__((naked)) FP_Multiply(int32_t a, int32_t b) {
  asm volatile (
  "movw  r16, r18 \n\t"
  "movw  r18, r20 \n\t"
  "movw  r20, r22 \n\t"
  "movw  r22, r24 \n\t"
  "clt            \n\t"
  //__mulusa3_round
  "clr  r24       \n\t"
  "clr  r25       \n\t"
  "mul  r16, r20  \n\t"
  "movw r26, r0   \n\t"
  "mul  r17, r20  \n\t"
  "add  r27, r0   \n\t"
  "adc  r24, r1   \n\t"
  "mul  r16, r21  \n\t"
  "add  r27, r0   \n\t"
  "adc  r24, r1   \n\t"
  "rol  r25       \n\t"
  "brtc 0f        \n\t"
  "sbrc r27, 0x07 \n\t"
  "adiw r24, 0x01 \n\t"
  "0: push r27    \n\t"
  "mul  R16, R22  \n\t"
  "add  R24, r0   \n\t"
  "adc  R25, r1   \n\t"
  "sbc  R26, R26  \n\t"
  "mul  R17, R21  \n\t"
  "add  R24, r0   \n\t"
  "adc  R25, r1   \n\t"
  "sbci R26, 0x00 \n\t"
  "mul  R18, R20  \n\t"
  "add  R24, r0   \n\t"
  "adc  R25, r1   \n\t"
  "sbci R26, 0x00 \n\t"
  "neg  R26       \n\t"
  "mul  R16, R23  \n\t"
  "add  R25, r0   \n\t"
  "adc  R26, r1   \n\t"
  "sbc  R27, R27  \n\t"
  "mul  R17, R22  \n\t"
  "add  R25, r0   \n\t"
  "adc  R26, r1   \n\t"
  "sbci R27, 0x00 \n\t"
  "mul  R18, R21  \n\t"
  "add  R25, r0   \n\t"
  "adc  R26, r1   \n\t"
  "sbci R27, 0x00 \n\t"
  "mul  R19, R20  \n\t"
  "add  R25, r0   \n\t"
  "adc  R26, r1   \n\t"
  "sbci R27, 0x00 \n\t"
  "neg  R27       \n\t"
  "mul  R17, R23  \n\t"
  "add  R26, r0   \n\t"
  "adc  R27, r1   \n\t"
  "mul  R18, R22  \n\t"
  "add  R26, r0   \n\t"
  "adc  R27, r1   \n\t"
  "mul  R19, R21  \n\t"
  "add  R26, r0   \n\t"
  "adc  R27, r1   \n\t"
  "mul  R18, R23  \n\t"
  "add  R27, r0   \n\t"
  "mul  R19, R22  \n\t"
  "add  R27, r0   \n\t"
  "pop  r0        \n\t"
  "clr  r1        \n\t"
  "tst  r23       \n\t"
  "brpl 1f        \n\t"
  "sub  r26, r16  \n\t"
  "sbc  r27, r17  \n\t"
  "1: sbrs r19, 0x07 \n\t"
  "rjmp 2f        \n\t"
  "sub  r26, r20  \n\t"
  "sbc  r27, r21  \n\t"
  "2: lsl  r0     \n\t"
  "rol  r24       \n\t"
  "rol  r25       \n\t"
  "rol  r26       \n\t"
  "rol  r27       \n\t"
  "lsl  r0        \n\t"
  "adc  r24, r1   \n\t"
  "adc  r25, r1   \n\t"
  "adc  r26, r1   \n\t"
  "adc  r27, r1   \n\t"
  "movw r22, r24  \n\t"
  "movw r24, r26  \n\t"
  "ret            \n\t"
  );
}

//__divsa3
int32_t __attribute__((naked)) FP_Division(int32_t a, int32_t b) {
  asm volatile (
  "movw  r26, r24 \n\t"
  "movw  r24, r22 \n\t"
  "mov  r0, r27   \n\t"
  "eor  r0, r21   \n\t"
  "sbrs r21, 0x07 \n\t"
  "rjmp 1f        \n\t"
  //NEG4 r18
  "com  R21       \n\t"
  "com  R20       \n\t"
  "com  R19       \n\t"
  "neg  R18       \n\t"
  "sbci R19, 0xFF \n\t"
  "sbci R20, 0xFF \n\t"
  "sbci R21, 0xFF \n\t"
  "1: sbrs r27, 0x07 \n\t"
  "rjmp    2f     \n\t"
  //NEG4 r24
  "com  R27       \n\t"
  "com  R26       \n\t"
  "com  R25       \n\t"
  "neg  R24       \n\t"
  "sbci R25, 0xFF \n\t"
  "sbci R26, 0xFF \n\t"
  "sbci R27, 0xFF \n\t"
  //__udivusa3
  "2: ldi r30, 0x20 \n\t"
  "mov  r1, r30   \n\t"
  "clr  r30       \n\t"
  "clr  r31       \n\t"
  "movw r22, r30  \n\t"
  "lsl  r24       \n\t"
  "rol  r25       \n\t"
  "udivusa3_loop: rol r26 \n\t"
  "rol  r27       \n\t"
  "rol  r30       \n\t"
  "rol  r31       \n\t"
  "brcs udivusa3_ep \n\t"
  "cp   r26, r18  \n\t"
  "cpc  r27, r19  \n\t"
  "cpc  r30, r20  \n\t"
  "cpc  r31, r21  \n\t"
  "brcc udivusa3_ep \n\t"
  "rol  r22       \n\t"
  "rjmp udivusa3_cont \n\t"
  "udivusa3_ep: sub  r26, r18 \n\t"
  "sbc  r27, r19  \n\t"
  "sbc  r30, r20  \n\t"
  "sbc  r31, r21  \n\t"
  "lsl  r22       \n\t"
  "udivusa3_cont: rol r23 \n\t"
  "rol  r24       \n\t"
  "rol  r25       \n\t"
  "dec  r1        \n\t"
  "brne udivusa3_loop \n\t"
  "com  r22       \n\t"
  "com  r23       \n\t"
  "com  r24       \n\t"
  "com  r25       \n\t"
  //"ret            \n\t"
  "lsr  r25       \n\t"
  "ror  r24       \n\t"
  "ror  r23       \n\t"
  "ror  r22       \n\t"
  "sbrs r0, 0x07  \n\t"
  "ret            \n\t"
  //negate r22
  //XJMP __negsi2
  );
}

//Difference from ISO/IEC DTR 18037: using an uint8_t as second parameter according to microcontroller register size and maximum possible value
int32_t FP_Round(int32_t f, uint8_t n) {
  n = FP_FBITS - n;
  if (f >= 0)
    return (f&(0xFFFFFFFF<<n)) + ((f&(1<<(n - 1)))<<1);
  else
    return (f&(0xFFFFFFFF<<n)) - ((f&(1<<(n - 1)))<<1);
}

//__fractsfsa
int32_t __attribute__((naked)) FP_FloatToFixed(float f) {
  asm volatile (
  "subi r24, 0x80   \n\t"
  "sbci r25, 0xf8   \n\t"
  "jmp fixsfsi      \n\t"
  "fixsfsi:         \n\t"
  "rcall fixunssfi  \n\t"
  "set              \n\t"
  "cpse r27, r1     \n\t"
  "rjmp fp_zero     \n\t"
  "ret              \n\t"
  "fixunssfi:       \n\t"
  "rcall fp_splitA  \n\t"
  "brcs 7f          \n\t"
  "subi r25, 127    \n\t"
  "brlo 8f          \n\t"
  "mov  r27, r25    \n\t"
  "clr  r25         \n\t"
  "subi r27, 23     \n\t"
  "brlo 4f          \n\t"
  "breq 9f          \n\t"
  "1: lsl r22       \n\t"
  "rol  r23         \n\t"
  "rol  r24         \n\t"
  "rol  r25         \n\t"
  "brmi 2f          \n\t"
  "dec  r27         \n\t"
  "brne 1b          \n\t"
  "rjmp 9f          \n\t"
  "2: cpi r27, 0x01 \n\t"
  "breq 9f          \n\t"
  "7: rcall fp_zero \n\t"
  "ldi  r27, 1      \n\t"
  "ret              \n\t"
  "8: rjmp fp_zero  \n\t"
  "3: mov r22, r23  \n\t"
  "mov  r23, r24    \n\t"
  "clr  r24         \n\t"
  "subi r27, -8     \n\t"
  "breq 9f          \n\t"
  "4: cpi r27, -7   \n\t"
  "brlt 3b          \n\t"
  "5: lsr r24       \n\t"
  "ror  r23         \n\t"
  "ror  r22         \n\t"
  "inc  r27         \n\t"
  "brne 5b          \n\t"
  "9: brtc 6f       \n\t"
  "com  r25         \n\t"
  "com  r24         \n\t"
  "com  r23         \n\t"
  "neg  r22         \n\t"
  "sbci r23, -1     \n\t"
  "sbci r24, -1     \n\t"
  "sbci r25, -1     \n\t"
  "6: ret           \n\t"
  "fp_split3:       \n\t"
  "sbrc r21, 0x07   \n\t"
  "subi r25, 0x80   \n\t"
  "lsl  r20         \n\t"
  "rol  r21         \n\t"
  "breq 14f         \n\t"
  "cpi  r21, 0xff   \n\t"
  "breq 15f         \n\t"
  "11: ror  r20     \n\t"
  "fp_splitA:       \n\t"
  "lsl  r24         \n\t"
  "12: bst r25, 0x07 \n\t"
  "rol  r25         \n\t"
  "breq 16f         \n\t"
  "cpi  r25, 0xff   \n\t"
  "breq 17f         \n\t"
  "13: ror r24      \n\t"
  "ret              \n\t"
  "14: cp r1, r18   \n\t"
  "cpc  r1, r19     \n\t"
  "cpc  r1, r20     \n\t"
  "rol  r21         \n\t"
  "rjmp 11b         \n\t"
  "15: lsr  r20     \n\t"
  "rcall fp_splitA  \n\t"
  "rjmp 18f         \n\t"
  "16: cp r1, r22   \n\t"
  "cpc  r1, r23     \n\t"
  "cpc  r1, r24     \n\t"
  "rol  r25         \n\t"
  "rjmp 13b         \n\t"
  "17: lsr r24      \n\t"
  "cpc  r23, r1     \n\t"
  "cpc  r22, r1     \n\t"
  "18: sec          \n\t"
  "ret              \n\t"
  "fp_zero:         \n\t"
  "clt              \n\t"
  "clr  r27         \n\t"
  "clr  r22         \n\t"
  "clr  r23         \n\t"
  "movw r24, r22    \n\t"
  "bld  r25, 0x07   \n\t"
  "ret              \n\t"
  );
}

//__fractsasf
float __attribute__((naked)) FP_FixedToFloat(int32_t k) {
  asm volatile (
  //__floatsisf:
  //"clt               \n\t"
  //"rjmp 1f           \n\t"
  "bst  r25, 0x07    \n\t"
  "brtc 1f           \n\t"
  "com  r25          \n\t"
  "com  r24          \n\t"
  "com  r23          \n\t"
  "neg  r22          \n\t"
  "sbci r23, -1      \n\t"
  "sbci r24, -1      \n\t"
  "sbci r25, -1      \n\t"
  "1: tst  r25       \n\t"
  "breq 4f           \n\t"
  "mov  r31, r25     \n\t"
  "ldi  r25, 127 + 23 \n\t"
  "clr  r27          \n\t"
  "2: inc  r25       \n\t"
  "lsr  r31          \n\t"
  "ror  r24          \n\t"
  "ror  r23          \n\t"
  "ror  r22          \n\t"
  "ror  r27          \n\t"
  "cpse r31, r1      \n\t"
  "rjmp  2b          \n\t"
  "brpl 11f          \n\t"
  "lsl  r27          \n\t"
  "brne 3f           \n\t"
  "sbrs  r22, 0x00   \n\t"
  "rjmp  11f         \n\t"
  "3: subi  r22, -1  \n\t"
  "sbci  r23, -1     \n\t"
  "sbci  r24, -1     \n\t"
  "sbci  r25, -1     \n\t"
  "rjmp  11f         \n\t"
  "4: tst  r24       \n\t"
  "breq 5f           \n\t"
  "ldi  r25, 127 + 23 \n\t"
  "rjmp 8f           \n\t"
  "5: tst  r23       \n\t"
  "breq 6f           \n\t"
  "ldi  r25, 127 + 15 \n\t"
  "mov  r24, r23     \n\t"
  "mov  r23, r22     \n\t"
  "rjmp 7f           \n\t"
  "6: tst  r22       \n\t"
  "breq 9f           \n\t"
  "ldi  r25, 127 + 7 \n\t"
  "mov  r24, r22     \n\t"
  "ldi  r23, 0x00    \n\t"
  "7: ldi  r22, 0x00 \n\t"
  "brmi 11f          \n\t"
  "10: dec  r25      \n\t"
  "lsl  r22          \n\t"
  "rol  r23          \n\t"
  "rol  r24          \n\t"
  "8: brpl 10b       \n\t"
  "11: lsl  r24      \n\t"
  "lsr  r25          \n\t"
  "ror  r24          \n\t"
  "bld  r25, 0x07    \n\t"
  "9: tst r25        \n\t"
  "breq 10f          \n\t"
  "subi r24, 0x80    \n\t"
  "sbci r25, 0x07    \n\t"
  "10: ret           \n\t"
  );
}

int32_t FP_Sin(int32_t fp) {
  int16_t sign = 1;
  int32_t sqr, result;
  const int32_t SK[2] = {
    FP_CONST(7.61e-03),
    FP_CONST(1.6605e-01)
  };

  //normalize
  fp %= 2*FP_PI;
  if (fp < 0)
    fp = FP_TWO_PI + fp;
    //fp = FP_PI*2 + fp;
  if ((fp > FP_HALF_PI) && (fp <= FP_PI))
    fp = FP_PI - fp;
  else if ((fp > FP_PI) && (fp <= (FP_PI + FP_HALF_PI))) {
    fp = fp - FP_PI;
    sign = -1;
  } else if (fp > (FP_PI + FP_HALF_PI)) {
    fp = (FP_PI<<1) - fp;
    sign = -1;
  }
  
  //calculate sine
  sqr = FP_Multiply(fp, fp);
  result = FP_Multiply(SK[0], sqr);
  result = FP_Multiply((result - SK[1]), sqr);
  result = FP_Multiply((result + FP_ONE), fp);
/*
  //taylor series
  // sin(x) = x − (x^3)/3! + (x^5)/5! − (x^7)/7! + ...
  sqr = FP_Multiply(fp, fp);
  fp = FP_Multiply(fp, sqr);
  result -= FP_Division(fp, itok(6));
  fp = FP_Multiply(fp, sqr);
  result += FP_Division(fp, itok(120));
  fp = FP_Multiply(fp, sqr);
  result -= FP_Division(fp, itok(5040));
  fp = FP_Multiply(fp, sqr);
  result += FP_Division(fp, itok(362880));
  fp = FP_Multiply(fp, sqr);
  result -= FP_Division(fp, itok(39916800));
*/
  return (sign*result);
}

#define _SqrtStep(shift)                  \
if ((0x40000001>>shift) + sval <= val) {  \
  val -= (0x40000001>>shift) + sval;      \
  sval = (sval>>1) | (0x40000001>>shift); \
  } else {                                \
  sval = sval>>1;                         \
}

int32_t _FP_SquareRoot(int32_t val, int32_t Q) {
  int32_t sval = 0;

  //convert Q to even
  if (Q & 0x01) {
    Q -= 1;
    val >>= 1;
  }
  //integer square root math
  for (uint8_t i=0; i<=30; i+=2)
    _SqrtStep(i);
  if (sval < val) {
    ++sval;
  }  
  //this is the square root in Q format
  sval <<= (Q)/2;
  //convert the square root to Q15 format
  if (Q < 15) {
    return(sval<<(15 - Q));
  } else {
    return(sval>>(Q - 15));
  }  
}

void setup(void) {
  volatile int32_t fix1, fix2, fix3;
  volatile float float1=0.66, float2=2.33, float3;
  
  fix1 = ftok(0.66); 
  fix2 = FloatToFixed(float2);
  
  fix3 = fix2 + fix1;
  float3 = FixedToFloat(fix3);
  
  fix3 = fix2 - fix1;
  float3 = FixedToFloat(fix3);
  
  fix3 = FP_Multiply(fix2, fix1);
  float3 = FixedToFloat(fix3);
  
  fix3 = FP_Division(fix2, fix1);
  float3 = FixedToFloat(fix3);
 
  fix1= ftok(30.25); 
  fix2 = FP_Sqrt(fix1);
  float1 = FP_FixedToFloat(fix2);    

  fix1 = FP_DegToRad(FP_FloatToFixed(45.0));
  float1 = FP_FixedToFloat(fix1);
  fix2 = FP_Sin(fix1);
  float2 = FP_FixedToFloat(fix2);

  float3 = float2 + float1;
  fix3 = FloatToFixed(float3);

  float3 = float2 - float1;
  fix3 = FloatToFixed(float3);

  float3 = float2 * float1;
  fix3 = FloatToFixed(float3);

  float3 = float2 / float1;
  fix3 = FloatToFixed(float3);
}

void loop(void) { }
Posted in Uncategorized | Tagged , , , , , , , , , , , , , , | Leave a comment

AVR GCC Fixed-Point vs. Floating Point Comparison

apples vs. oranges

This is a follow up to previous posts here and here. Using native fixed point support in GCC, on a generic ATMega328P running 16MHz on the AtmelStudio 6.2 simulator, I performed this overly simplified comparison of fixed vs. floating point math. The results posted below compare the fixed point accum type with a float.

The accum typedef allows for an unsigned 16.16 fixed format (+/-16.15 if signed). The fract typedef provides an unsigned 0.16 format (+/-0.15 if signed). Short, long and long long modifiers are also allowed.

It is very difficult to find supporting documentation on (AVR) fixed point. A basic overview of the types supported is here and here. The ISO standard for C (embedded extensions) fixed point is located here.

#include <avr/io.h>
#include <stdfix.h>

int main(void) {
  volatile accum fx1, fx2 = 2.33K, fx3 = 0.66K;
  volatile float fl1, flt2 = 2.33, fl3 = 0.66;
  
  fx1 = fx2 + fx3;
  fl1 = fl2 + fl3;

  fx1 = fx2 – fx3;
  fl1 = fl2 - fl3;

  fx1 = fx2 * fx3;
  fl1 = fl2 * fl3;

  fx3 = fx2 / fx3;
  fl3 = fl2 / fl3;

  //fl1 = (float)fx3;
}

In all cases except division, the fixed point versions are faster. All fixed point versions produce smaller code. The use of a fract type can further reduce code size and improve speed (Note: since fract division is a 16-bit version, speed is increased and size is reduced compared to floating point). Obviously, the accuracy/precision is lacking in all cases with fixed point. The results are summarized below:

Addition
Fixed-point results:
= 2.98996
27 cycles or 1.69us (4.5x faster)
code size: 238 bytes

Floating-point results:
= 2.99
123 cycles or 7.69us
Code size: 598 bytes

Subtraction
Fixed-point results:
= 1.670013
27 cycles or 1.69us (4.8x faster)
code size: 238 bytes

Floating-point results:
= 1.67
131 cycles or 8.19us
Code size: 598 bytes

Multiplication
Fixed-point results:
= 5.428833
132 cycles or 8.25us (12% faster)
code size: 394 bytes

Floating-point results:
= 5.428837
156 cycles or 9.25us
Code size; 578 bytes

Division
Fixed-point results:
= 3.530426
700 cycles or 43.75us (30% slower)
code size: 378 bytes

Floating-point results:
= 3.530303
492 cycles or 30.75us
Code size: 604 bytes

Note: To my knowledge, this will not compile with the Arduino IDE because the arduino IDE will not link to the AVR libm library which contains the fixed point routines. See my posting here for arduino compatible routines.

Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

Debugging an LPC810 Breakout Board via SWD/J-Link

I’m playing with a LPC810 Breakout Board connected with the IAR EW IDE via a SEGGER J-Link debugger utilizing SWD. The J-Link is connected via a small 20 to 10-pin adapter board. Since I am unable to power the µC via the 3.3V J-Link pin (not sure why), I’m also using a USB connection with the PC to provide power to the board.

Contrary to the User Manual, I needed to enable the SWDIO and SWCLK pins on the LPC810 to get this to work. After compiling inside the IAR EW IDE, I upload the iHex format file to the µC via the FlashMagic program. When the debugger is started, it outputs a warning about the target not having enough memory. I simply ignore the warning and it all seems to work okay. Debugging is with full symbol support, and I’m able to set breakpoints once inside the debugger.

In the picture, the µC is running a simplified version of a LED blink program.

LPC810 BoB J-Link SWD Debugging

Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

LPC812 on a 20MHz External Crystal

oscillators

Because the NXP LPC800 clock options and the setup are somewhat confusing, I thought I would post this example. Here is my LPC812 dev board running on a 20MHz external crystal. The frequency counter is reading the clockout pin which is divided by 2. The clock and pin setup code is below.

LPC812 at 20MHZ

//----------------------------------------------------------------------------
// Name:    Blinky_PLL_Test.c
// Purpose: LED Flasher
// pin #6 set for LED
// pin #1 set for grounding xtal
// pin #15 set to CLKOUT
//----------------------------------------------------------------------------
#include <stdint.h>
#include <string.h>
#include "LPC812.h" //my bare metal version

//our systick interrupt handler
void SysTick_Handler(void) {
  //toggle led pin
  GPIO_NOT = (1<<6);
}

int main(void) {
  uint32_t reg;
 
  //init gpio port
  GpioInit();
  //make gpio #6 pin an output (physical pin #10)
  GPIO_DIR |= (1<<6);

  //enable AHB clock to SWM and IOCON
  SYSCON_SYSAHBCLKCTRL |= (1<<SWM) | (1<<IOCON);

  //
  //external crystal setup
  //
  
//pin modes
#define INACTIVE_MODE  0x00
#define PULL_DOWN_MODE 0x01
#define PULL_UP_MODE   0x02
#define REPEATER_MODE  0x03
  //set pull-down on pio01 for ext crystal grounding pin
  reg = IOCON_PIO0_1 & ~(0x3<<3);
  IOCON_PIO0_1 = reg | (PULL_DOWN_MODE<<3);
  GPIO0_1 = 0;

  //disable pull-up/down on pio08/9
  reg = IOCON_PIO0_8 & ~(0x3<<3);
  IOCON_PIO0_8 = reg | (INACTIVE_MODE<<3);
  reg = IOCON_PIO0_9 & ~(0x3<<3);
  IOCON_PIO0_9 = reg | (INACTIVE_MODE<<3);
  //enable XTALIN & XTALOUT on PIO08 & PIO09
  PINENABLE0 &= ~((1<<(uint32_t)4) | (~0x1ff));
  PINENABLE0 &= ~((1<<(uint32_t)5) | (~0x1ff));

  //
  //sys osc setup
  //
  
  //sys osc bypass options
#define SYSOSC_BYPASS_DISABLE 0
#define SYSOSC_BYPASS_ENABLE  1 //pll fed directly from xtal
  //sys osc bypass 
  SYSCON_SYSOSCCTRL |= SYSOSC_BYPASS_DISABLE;
  //power up sys osc
  reg = SYSCON_PDRUNCFG & (0x000080ef);
  reg &= ~((1<<5)&0x000080ef);
  SYSCON_PDRUNCFG = (0x00006d10 | reg);
  //delay for power up
  for (reg=0; reg<200; reg++)
    asm("nop");
  
  //
  //PLL setup
  //

  //power-up PLL 
  reg = SYSCON_PDRUNCFG & (0x000080ef);
  reg &= ~((1<<7)&0x000080ef);
  SYSCON_PDRUNCFG = (0x00006d10 | reg);
//pll input options
#define PLL_IRC    0UL
#define PLL_SYSOSC 1UL
#define PLL_CLKIN  3UL
  //set sys osc as input to PLL & toggle update enable 
  SYSCON_SYSPLLCLKSEL = PLL_SYSOSC;
  //toggle to enable update
  SYSCON_SYSPLLCLKUEN = 0UL;
  SYSCON_SYSPLLCLKUEN = 1UL;
  //set PLL frequency
  SYSCON_SYSPLLCTRL = (0 + (0<<5));
  //wait until PLL locked
  while (!(SYSCON_SYSPLLSTAT&0x01));

  //
  //main clock setup
  //
  
//main clock input options
#define MAINCLK_IRC     0
#define MAINCLK_PLL_IN  1 //sys osc (irc, xtal or clkin?)
#define MAINCLK_WDG     2
#define MAINCLK_PLL_OUT 3
  //set pll input to main clock & toggle update enable
  SYSCON_MAINCLKSEL = MAINCLK_PLL_OUT;
  //toggle to enable update
  SYSCON_MAINCLKUEN = 0UL;
  SYSCON_MAINCLKUEN = 1UL;
  //wait until update complted
  while (!(SYSCON_MAINCLKUEN&0x01));
  //set system clock divider 
  SYSCON_SYSAHBCLKDIV = 1;
  //main clock speed
  SystemMainClock = 20000000UL*((SYSCON_SYSPLLCTRL&0x1f) + 1);
  //core clock speed
  SystemCoreClock = SystemMainClock/SYSCON_SYSAHBCLKDIV;

  //
  //clockout pin setup
  //
 	
  //configure PIO015 with pull-up 
  reg = IOCON_PIO0_15 & ~(0x3<<3);
  IOCON_PIO0_15 = reg | (PULL_UP_MODE<<3);
  //assign CLKOUT to PIO0_15 
  reg = PINASSIGN8 & (~(0xff<<0x10));
  PINASSIGN8 = reg | (0xf<<0x10);
//clockout sources
#define CLKOUT_IRC        0 //Internal oscillator for CLKOUT 
#define CLKOUT_SYSOSC     1 //System oscillator for CLKOUT 
#define CLKOUT_WDTOSC     2 //Watchdog oscillator for CLKOUT
#define CLKOUT_MAINSYSCLK 3 //Main system clock for CLKOUT 
  //set CLKOUT pin source
  SYSCON_CLKOUTSEL = (uint32_t)CLKOUT_MAINSYSCLK;
  //toggle to enable update
  SYSCON_CLKOUTUEN = 0UL;
  SYSCON_CLKOUTUEN = 1UL;
  //set clockout divider
  SYSCON_CLKOUTDIV = 2;

  //disable clock to SWM peripheral
  SYSCON_SYSAHBCLKCTRL &= ~(1<<SWM);

  //set systick clock interrupt to Hz
  SysTickConfig(1000000UL);
  
  //loop forever
  while(1)
    asm("wfi"); //wait for interrupt
}

LPC812 defines file:

//LPC812.h

// GPIO_PORTs
#define GPIO_BASE            0xA0000000
#define GPIO0_0              (*((volatile unsigned char *)(GPIO_BASE + 0x00)))
#define GPIO0_1              (*((volatile unsigned char *)(GPIO_BASE + 0x01)))
#define GPIO0_2              (*((volatile unsigned char *)(GPIO_BASE + 0x02)))
#define GPIO0_3              (*((volatile unsigned char *)(GPIO_BASE + 0x03)))
#define GPIO0_4              (*((volatile unsigned char *)(GPIO_BASE + 0x04)))
#define GPIO0_5              (*((volatile unsigned char *)(GPIO_BASE + 0x05)))
#define GPIO0_6              (*((volatile unsigned char *)(GPIO_BASE + 0x06)))
#define GPIO0_7              (*((volatile unsigned char *)(GPIO_BASE + 0x07)))
#define GPIO0_8              (*((volatile unsigned char *)(GPIO_BASE + 0x08)))
#define GPIO0_9              (*((volatile unsigned char *)(GPIO_BASE + 0x09)))
#define GPIO0_10             (*((volatile unsigned char *)(GPIO_BASE + 0x0A)))
#define GPIO0_11             (*((volatile unsigned char *)(GPIO_BASE + 0x0B)))
#define GPIO0_12             (*((volatile unsigned char *)(GPIO_BASE + 0x0C)))
#define GPIO0_13             (*((volatile unsigned char *)(GPIO_BASE + 0x0D)))
#define GPIO0_14             (*((volatile unsigned char *)(GPIO_BASE + 0x0E)))
#define GPIO0_15             (*((volatile unsigned char *)(GPIO_BASE + 0x0F)))
#define GPIO0_16             (*((volatile unsigned char *)(GPIO_BASE + 0x10)))
#define GPIO0_17             (*((volatile unsigned char *)(GPIO_BASE + 0x11)))
#define GPIO_DIR             (*((volatile unsigned int *)(GPIO_BASE + 0x2000)))
#define GPIO_MASK            (*((volatile unsigned int *)(GPIO_BASE + 0x2080)))
#define GPIO_PORT            (*((volatile unsigned int *)(GPIO_BASE + 0x2100)))
#define GPIO_MPORT           (*((volatile unsigned int *)(GPIO_BASE + 0x2180)))
#define GPIO_SET             (*((volatile unsigned int *)(GPIO_BASE + 0x2200)))
#define GPIO_CLR             (*((volatile unsigned int *)(GPIO_BASE + 0x2280)))
#define GPIO_NOT             (*((volatile unsigned int *)(GPIO_BASE + 0x2300)))

// Switch Matrix
#define SWM_BASE             0x4000C000
#define PINASSIGN0           (*((volatile unsigned int *)(SWM_BASE + 0x000)))
#define PINASSIGN1           (*((volatile unsigned int *)(SWM_BASE + 0x004)))
#define PINASSIGN2           (*((volatile unsigned int *)(SWM_BASE + 0x008)))
#define PINASSIGN3           (*((volatile unsigned int *)(SWM_BASE + 0x00c)))
#define PINASSIGN4           (*((volatile unsigned int *)(SWM_BASE + 0x010)))
#define PINASSIGN5           (*((volatile unsigned int *)(SWM_BASE + 0x014)))
#define PINASSIGN6           (*((volatile unsigned int *)(SWM_BASE + 0x018)))
#define PINASSIGN7           (*((volatile unsigned int *)(SWM_BASE + 0x01c)))
#define PINASSIGN8           (*((volatile unsigned int *)(SWM_BASE + 0x020)))
#define PINENABLE0           (*((volatile unsigned int *)(SWM_BASE + 0x1c0)))

// SCTimer/PWM
#define SCT_BASE             (0x50004000UL)
#define SCT_CONFIG           (*((volatile unsigned int *)(SCT_BASE + 0x000)))
#define SCT_CTRL             (*((volatile unsigned int *)(SCT_BASE + 0x004)))
#define SCT_LIMIT            (*((volatile unsigned int *)(SCT_BASE + 0x008)))
#define SCT_HALT             (*((volatile unsigned int *)(SCT_BASE + 0x00C)))
#define SCT_STOP             (*((volatile unsigned int *)(SCT_BASE + 0x010)))
#define SCT_START            (*((volatile unsigned int *)(SCT_BASE + 0x014)))
#define SCT_COUNT            (*((volatile unsigned int *)(SCT_BASE + 0x040)))
#define SCT_STATE            (*((volatile unsigned int *)(SCT_BASE + 0x044)))
#define SCT_INPUT            (*((volatile unsigned int *)(SCT_BASE + 0x048)))
#define SCT_REGMODE          (*((volatile unsigned int *)(SCT_BASE + 0x04C)))
#define SCT_OUTPUT           (*((volatile unsigned int *)(SCT_BASE + 0x050)))
#define SCT_OUTPUTDIRCTRL    (*((volatile unsigned int *)(SCT_BASE + 0x054)))
#define SCT_RES              (*((volatile unsigned int *)(SCT_BASE + 0x058)))
#define SCT_EVEN             (*((volatile unsigned int *)(SCT_BASE + 0x0F0)))
#define SCT_EVFLAG           (*((volatile unsigned int *)(SCT_BASE + 0x0F4)))
#define SCT_CONEN            (*((volatile unsigned int *)(SCT_BASE + 0x0F8)))
#define SCT_CONFLAG          (*((volatile unsigned int *)(SCT_BASE + 0x0FC)))
#define SCT_MATCH0           (*((volatile unsigned int *)(SCT_BASE + 0x100)))
#define SCT_MATCH1           (*((volatile unsigned int *)(SCT_BASE + 0x104)))
#define SCT_MATCH2           (*((volatile unsigned int *)(SCT_BASE + 0x108)))
#define SCT_MATCH3           (*((volatile unsigned int *)(SCT_BASE + 0x10C)))
#define SCT_MATCH4           (*((volatile unsigned int *)(SCT_BASE + 0x110)))
#define SCT_CAP0             (*((volatile unsigned int *)(SCT_BASE + 0x100)))
#define SCT_CAP1             (*((volatile unsigned int *)(SCT_BASE + 0x104)))
#define SCT_CAP2             (*((volatile unsigned int *)(SCT_BASE + 0x108)))
#define SCT_CAP3             (*((volatile unsigned int *)(SCT_BASE + 0x10C)))
#define SCT_CAP4             (*((volatile unsigned int *)(SCT_BASE + 0x110)))
#define SCT_MATCHREL0        (*((volatile unsigned int *)(SCT_BASE + 0x200)))
#define SCT_MATCHREL1        (*((volatile unsigned int *)(SCT_BASE + 0x204)))
#define SCT_MATCHREL2        (*((volatile unsigned int *)(SCT_BASE + 0x208)))
#define SCT_MATCHREL3        (*((volatile unsigned int *)(SCT_BASE + 0x20C)))
#define SCT_MATCHREL4        (*((volatile unsigned int *)(SCT_BASE + 0x210)))
#define SCT_CAPCTRL0         (*((volatile unsigned int *)(SCT_BASE + 0x200)))
#define SCT_CAPCTRL1         (*((volatile unsigned int *)(SCT_BASE + 0x204)))
#define SCT_CAPCTRL2         (*((volatile unsigned int *)(SCT_BASE + 0x208)))
#define SCT_CAPCTRL3         (*((volatile unsigned int *)(SCT_BASE + 0x20C)))
#define SCT_CAPCTRL4         (*((volatile unsigned int *)(SCT_BASE + 0x210)))
#define SCT_EV0_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x300)))
#define SCT_EV1_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x308)))
#define SCT_EV2_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x310)))
#define SCT_EV3_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x318)))
#define SCT_EV4_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x320)))
#define SCT_EV5_STATE        (*((volatile unsigned int *)(SCT_BASE + 0x328)))
#define SCT_EV0_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x304)))
#define SCT_EV1_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x30C)))
#define SCT_EV2_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x314)))
#define SCT_EV3_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x31C)))
#define SCT_EV4_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x324)))
#define SCT_EV5_CTRL         (*((volatile unsigned int *)(SCT_BASE + 0x32C)))
#define SCT_OUT0_SET         (*((volatile unsigned int *)(SCT_BASE + 0x500)))
#define SCT_OUT1_SET         (*((volatile unsigned int *)(SCT_BASE + 0x508)))
#define SCT_OUT2_SET         (*((volatile unsigned int *)(SCT_BASE + 0x510)))
#define SCT_OUT3_SET         (*((volatile unsigned int *)(SCT_BASE + 0x518)))
#define SCT_OUT0_CLR         (*((volatile unsigned int *)(SCT_BASE + 0x504)))
#define SCT_OUT1_CLR         (*((volatile unsigned int *)(SCT_BASE + 0x50C)))
#define SCT_OUT2_CLR         (*((volatile unsigned int *)(SCT_BASE + 0x514)))
#define SCT_OUT3_CLR         (*((volatile unsigned int *)(SCT_BASE + 0x51C)))

// USART0
#define USART0_BASE          0x40064000
#define USART0_CFG           (*((volatile unsigned int *)(USART0_BASE + 0x00)))
#define USART0_CTL           (*((volatile unsigned int *)(USART0_BASE + 0x04)))
#define USART0_STAT          (*((volatile unsigned int *)(USART0_BASE + 0x08)))
#define USART0_INTENSET      (*((volatile unsigned int *)(USART0_BASE + 0x0c)))
#define USART0_INTENCLR      (*((volatile unsigned int *)(USART0_BASE + 0x10)))
#define USART0_RXDAT         (*((volatile unsigned int *)(USART0_BASE + 0x14)))
#define USART0_RXDATSTAT     (*((volatile unsigned int *)(USART0_BASE + 0x18)))
#define USART0_TXDAT         (*((volatile unsigned int *)(USART0_BASE + 0x1c)))
#define USART0_BRG           (*((volatile unsigned int *)(USART0_BASE + 0x20)))
#define USART0_INTSTAT       (*((volatile unsigned int *)(USART0_BASE + 0x24)))

// USART1
#define USART1_BASE          0x40068000
#define USART1_CFG           (*((volatile unsigned int *)(USART1_BASE + 0x00)))
#define USART1_CTL           (*((volatile unsigned int *)(USART1_BASE + 0x04)))
#define USART1_STAT          (*((volatile unsigned int *)(USART1_BASE + 0x08)))
#define USART1_INTENSET      (*((volatile unsigned int *)(USART1_BASE + 0x0c)))
#define USART1_INTENCLR      (*((volatile unsigned int *)(USART1_BASE + 0x10)))
#define USART1_RXDAT         (*((volatile unsigned int *)(USART1_BASE + 0x14)))
#define USART1_RXDATSTAT     (*((volatile unsigned int *)(USART1_BASE + 0x18)))
#define USART1_TXDAT         (*((volatile unsigned int *)(USART1_BASE + 0x1c)))
#define USART1_BRG           (*((volatile unsigned int *)(USART1_BASE + 0x20)))
#define USART1_INTSTAT       (*((volatile unsigned int *)(USART1_BASE + 0x24)))

// USART2
#define USART2_BASE			     0x4006C000
#define USART2_CFG           (*((volatile unsigned int *)(USART2_BASE + 0x00)))
#define USART2_CTL           (*((volatile unsigned int *)(USART2_BASE + 0x04)))
#define USART2_STAT          (*((volatile unsigned int *)(USART2_BASE + 0x08)))
#define USART2_INTENSET      (*((volatile unsigned int *)(USART2_BASE + 0x0c)))
#define USART2_INTENCLR      (*((volatile unsigned int *)(USART2_BASE + 0x10)))
#define USART2_RXDAT         (*((volatile unsigned int *)(USART2_BASE + 0x14)))
#define USART2_RXDATSTA      (*((volatile unsigned int *)(USART2_BASE + 0x18)))
#define USART2_TXDAT         (*((volatile unsigned int *)(USART2_BASE + 0x1c)))
#define USART2_BRG           (*((volatile unsigned int *)(USART2_BASE + 0x20)))
#define USART2_INTSTAT       (*((volatile unsigned int *)(USART2_BASE + 0x24)))

// SYSCON
#define SYSCON_BASE          0x40048000
#define SYSCON_MAP           (*((volatile unsigned int *)(SYSCON_BASE + 0x000)))
#define SYSCON_PRESETCTRL    (*((volatile unsigned int *)(SYSCON_BASE + 0x004)))
#define SYSCON_SYSPLLCTRL    (*((volatile unsigned int *)(SYSCON_BASE + 0x008)))
#define SYSCON_SYSPLLSTAT    (*((volatile unsigned int *)(SYSCON_BASE + 0x00C)))
#define SYSCON_SYSOSCCTRL    (*((volatile unsigned int *)(SYSCON_BASE + 0x020)))
#define SYSCON_WDTOSCCTRL    (*((volatile unsigned int *)(SYSCON_BASE + 0x024)))
#define SYSCON_SYSRSTSTAT    (*((volatile unsigned int *)(SYSCON_BASE + 0x030)))
#define SYSCON_SYSPLLCLKSEL  (*((volatile unsigned int *)(SYSCON_BASE + 0x040)))
#define SYSCON_SYSPLLCLKUEN  (*((volatile unsigned int *)(SYSCON_BASE + 0x044)))
#define SYSCON_MAINCLKSEL    (*((volatile unsigned int *)(SYSCON_BASE + 0x070)))
#define SYSCON_MAINCLKUEN    (*((volatile unsigned int *)(SYSCON_BASE + 0x074)))
#define SYSCON_SYSAHBCLKDIV  (*((volatile unsigned int *)(SYSCON_BASE + 0x078)))
#define SYSCON_SYSAHBCLKCTRL (*((volatile unsigned int *)(SYSCON_BASE + 0x080)))
#define SYSCON_UARTCLKDIV    (*((volatile unsigned int *)(SYSCON_BASE + 0x094)))
#define SYSCON_CLKOUTSEL     (*((volatile unsigned int *)(SYSCON_BASE + 0x0E0)))
#define SYSCON_CLKOUTUEN     (*((volatile unsigned int *)(SYSCON_BASE + 0x0E4)))
#define SYSCON_CLKOUTDIV     (*((volatile unsigned int *)(SYSCON_BASE + 0x0E8)))
#define SYSCON_UARTFRGDIV    (*((volatile unsigned int *)(SYSCON_BASE + 0x0F0)))
#define SYSCON_UARTFRGMULT   (*((volatile unsigned int *)(SYSCON_BASE + 0x0F4)))
#define SYSCON_EXTTRACECMD   (*((volatile unsigned int *)(SYSCON_BASE + 0x0FC)))
#define SYSCON_PIOPORCAP0    (*((volatile unsigned int *)(SYSCON_BASE + 0x100)))
#define SYSCON_IOCONCLKDIV6  (*((volatile unsigned int *)(SYSCON_BASE + 0x134)))
#define SYSCON_IOCONCLKDIV5  (*((volatile unsigned int *)(SYSCON_BASE + 0x138)))
#define SYSCON_IOCONCLKDIV4  (*((volatile unsigned int *)(SYSCON_BASE + 0x13c)))
#define SYSCON_IOCONCLKDIV3  (*((volatile unsigned int *)(SYSCON_BASE + 0x140)))
#define SYSCON_IOCONCLKDIV2  (*((volatile unsigned int *)(SYSCON_BASE + 0x144)))
#define SYSCON_IOCONCLKDIV1  (*((volatile unsigned int *)(SYSCON_BASE + 0x148)))
#define SYSCON_IOCONCLKDIV0  (*((volatile unsigned int *)(SYSCON_BASE + 0x14C)))
#define SYSCON_BODCTRL       (*((volatile unsigned int *)(SYSCON_BASE + 0x150)))
#define SYSCON_SYSTCKCAL     (*((volatile unsigned int *)(SYSCON_BASE + 0x154)))
#define SYSCON_IRQLATENCY    (*((volatile unsigned int *)(SYSCON_BASE + 0x170)))
#define SYSCON_NMISRC        (*((volatile unsigned int *)(SYSCON_BASE + 0x174)))
#define SYSCON_PINTSEL0      (*((volatile unsigned int *)(SYSCON_BASE + 0x178)))
#define SYSCON_PINTSEL1      (*((volatile unsigned int *)(SYSCON_BASE + 0x17c)))
#define SYSCON_PINTSEL2      (*((volatile unsigned int *)(SYSCON_BASE + 0x180)))
#define SYSCON_PINTSEL3      (*((volatile unsigned int *)(SYSCON_BASE + 0x184)))
#define SYSCON_PINTSEL4      (*((volatile unsigned int *)(SYSCON_BASE + 0x188)))
#define SYSCON_PINTSEL5      (*((volatile unsigned int *)(SYSCON_BASE + 0x18c)))
#define SYSCON_PINTSEL6      (*((volatile unsigned int *)(SYSCON_BASE + 0x190)))
#define SYSCON_PINTSEL7      (*((volatile unsigned int *)(SYSCON_BASE + 0x194)))
#define SYSCON_STARTERP0     (*((volatile unsigned int *)(SYSCON_BASE + 0x204)))
#define SYSCON_STARTERP1     (*((volatile unsigned int *)(SYSCON_BASE + 0x214)))
#define SYSCON_PDSLEEPCFG    (*((volatile unsigned int *)(SYSCON_BASE + 0x230)))
#define SYSCON_PDAWAKECFG    (*((volatile unsigned int *)(SYSCON_BASE + 0x234)))
#define SYSCON_PDRUNCFG      (*((volatile unsigned int *)(SYSCON_BASE + 0x238)))
#define SYSCON_DEVICE_ID     (*((volatile unsigned int *)(SYSCON_BASE + 0x3f8)))

//MRT
#define MRT_INTVAL0          (*((volatile unsigned int *)0x40004000))
#define MRT_TIMER0           (*((volatile unsigned int *)0x40004004))
#define MRT_CTRL0            (*((volatile unsigned int *)0x40004008))
#define MRT_STAT0            (*((volatile unsigned int *)0x4000400C))
#define MRT_INTVAL1          (*((volatile unsigned int *)0x40004010))
#define MRT_TIMER1           (*((volatile unsigned int *)0x40004014))
#define MRT_CTRL1            (*((volatile unsigned int *)0x40004018))
#define MRT_STAT1            (*((volatile unsigned int *)0x4000401C))
#define MRT_INTVAL2          (*((volatile unsigned int *)0x40004020))
#define MRT_TIMER2           (*((volatile unsigned int *)0x40004024))
#define MRT_CTRL2            (*((volatile unsigned int *)0x40004028))
#define MRT_STAT2            (*((volatile unsigned int *)0x4000402C))
#define MRT_INTVAL3          (*((volatile unsigned int *)0x40004030))
#define MRT_TIMER3           (*((volatile unsigned int *)0x40004034))
#define MRT_CTRL3            (*((volatile unsigned int *)0x40004038))
#define MRT_STAT3            (*((volatile unsigned int *)0x4000403C))
#define MRT_IDLE_CH          (*((volatile unsigned int *)0x400040F4))
#define MRT_IRQ_FLAG         (*((volatile unsigned int *)0x400040F8))

// NVIC 
#define SYS_BASE             0xE000E000
#define NVIC_ST_CTRL         (*((volatile unsigned int *)(SYS_BASE + 0x010)))
#define NVIC_ST_RELOAD       (*((volatile unsigned int *)(SYS_BASE + 0x014))) 
#define NVIC_ST_CURRENT      (*((volatile unsigned int *)(SYS_BASE + 0x018))) 
#define NVIC_ST_CAL          (*((volatile unsigned int *)(SYS_BASE + 0x01C)))
#define NVIC_ISER0           (*((volatile unsigned int *)(SYS_BASE + 0x100)))
#define NVIC_ICER0           (*((volatile unsigned int *)(SYS_BASE + 0x180)))
#define NVIC_ISPR0           (*((volatile unsigned int *)(SYS_BASE + 0x200)))
#define NVIC_ICPR0           (*((volatile unsigned int *)(SYS_BASE + 0x280)))
#define NVIC_IABR0           (*((volatile unsigned int *)(SYS_BASE + 0x300)))
#define NVIC_IPR0            (*((volatile unsigned int *)(SYS_BASE + 0x400)))
#define NVIC_IPR1            (*((volatile unsigned int *)(SYS_BASE + 0x404)))
#define NVIC_IPR2            (*((volatile unsigned int *)(SYS_BASE + 0x408)))
#define NVIC_IPR3            (*((volatile unsigned int *)(SYS_BASE + 0x40c)))
#define NVIC_IPR6            (*((volatile unsigned int *)(SYS_BASE + 0x418)))
#define NVIC_IPR7            (*((volatile unsigned int *)(SYS_BASE + 0x41c)))
#define NVIC_ST_CTRL_CLK_SRC 0x00000004  //Clock Source
#define NVIC_ST_CTRL_ENABLE  0x00000001  //Enable
#define SCB_SHP0             (*((volatile unsigned int *)(0xE000ED1C)))
#define SCB_SHP1             (*((volatile unsigned int *)(0xE000ED20)))

//IOCON
#define IOCON_BASE           0x40044000
#define IOCON_PIO0_17        (*((volatile unsigned int *)(IOCON_BASE + 0x000)))
#define IOCON_PIO0_13        (*((volatile unsigned int *)(IOCON_BASE + 0x004)))
#define IOCON_PIO0_12        (*((volatile unsigned int *)(IOCON_BASE + 0x008)))
#define IOCON_PIO0_5         (*((volatile unsigned int *)(IOCON_BASE + 0x00C)))
#define IOCON_PIO0_4         (*((volatile unsigned int *)(IOCON_BASE + 0x010)))
#define IOCON_PIO0_3         (*((volatile unsigned int *)(IOCON_BASE + 0x014)))
#define IOCON_PIO0_2         (*((volatile unsigned int *)(IOCON_BASE + 0x018)))
#define IOCON_PIO0_11        (*((volatile unsigned int *)(IOCON_BASE + 0x01C)))
#define IOCON_PIO0_10        (*((volatile unsigned int *)(IOCON_BASE + 0x020)))
#define IOCON_PIO0_16        (*((volatile unsigned int *)(IOCON_BASE + 0x024)))
#define IOCON_PIO0_15        (*((volatile unsigned int *)(IOCON_BASE + 0x028)))
#define IOCON_PIO0_1         (*((volatile unsigned int *)(IOCON_BASE + 0x02c)))
#define IOCON_PIO0_9         (*((volatile unsigned int *)(IOCON_BASE + 0x034)))
#define IOCON_PIO0_8         (*((volatile unsigned int *)(IOCON_BASE + 0x038)))
#define IOCON_PIO0_7         (*((volatile unsigned int *)(IOCON_BASE + 0x03c)))
#define IOCON_PIO0_6         (*((volatile unsigned int *)(IOCON_BASE + 0x040)))
#define IOCON_PIO0_0         (*((volatile unsigned int *)(IOCON_BASE + 0x044)))
#define IOCON_PIO0_14        (*((volatile unsigned int *)(IOCON_BASE + 0x048)))

//Enable a device-specific interrupt in the NVIC interrupt controller.
static inline void NVIC_EnableIRQ(uint8_t IRQn) {
  NVIC_ISER0 = (1<<((uint32_t)(IRQn)&0x1F));
}

//System Tick Configuration
//Initializes the system timer, its interrupt, and starts timer.
//Counter in free running mode to generate periodic interrupts.
//ticks = number of ticks between two interrupts.
static inline uint32_t SysTickConfig(uint32_t ticks) {
  if (ticks > 0xFFFFFFUL)
    return (1); //reload value out of limit (24-bits)
  //set reload register
  NVIC_ST_RELOAD = (ticks&0xFFFFFFUL) - 1;
  //set IRQ priority
  SCB_SHP1 = 0xC0000000;
  //load the counter
  NVIC_ST_CURRENT = 0;
  //enable IRQ and go
  NVIC_ST_CTRL = 0x07;
  return 0;
}

//enable SysTick
void SysTickEnable(void) {
  NVIC_ST_CTRL |= NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE;
}

//disable SysTick
void SysTickDisable(void) {
  NVIC_ST_CTRL &= ~(NVIC_ST_CTRL_ENABLE);
}

void SysTickReset(void) {
  //clear counter
  NVIC_ST_CURRENT = 0; 
}

//System Clock Peripheral Control bits
#define SYS                  0
#define ROM                  1
#define RAM                  2
#define FLASHREG             3
#define FLASH                4
#define I2C                  5
#define GPIO                 6
#define SWM                  7
#define SCT                  8
#define WKT                  9
#define MRT                  10
#define SPIO                 11
#define SPI1                 12
#define CRC                  13
#define UART0                14
#define UART1                15
#define UART2                16
#define WWDT                 17
#define IOCON                18
#define ACMP                 19
//Peripheral Reset Control bits
#define SPIO0_RST_N          0
#define SPIO1_RST_N          1
#define UARTFRG_RST_N        2
#define UART0_RST_N          3
#define UART1_RST_N          4
#define UART2_RST_N          5
#define I2C_RST_N            6
#define MRT_RST_N            7
#define SCT_RST_N            8
#define WKT_RST_N            9
#define GPIO_RST_N           10
#define FLASH_RST_N          11
#define ACMP_RST_N           12

//set to default IRC speed upon reset
uint32_t SystemMainClock = 12000000UL;
uint32_t SystemCoreClock = 12000000UL;

void GpioInit(void) {
  //enable AHB clock to the GPIO domain
  SYSCON_SYSAHBCLKCTRL |= (1<<GPIO);
  //reset gpio peripheral control
  SYSCON_PRESETCTRL &= ~(1<<GPIO_RST_N);
  SYSCON_PRESETCTRL |= (1<<GPIO_RST_N);
}

//get port pin digital value
uint32_t GpioGetPin(uint32_t pinbit) {
  uint32_t result = 0;
  
  if (pinbit < 0x20)
    if (GPIO_PORT&(1<<pinbit))
      result = 1;
    else if (pinbit == 0xff)
      result = GPIO_PORT;
  return(result);
}
Posted in Uncategorized | Tagged , , , , , , , , , , , , | Leave a comment

LPC810 Breakout Board

LPC810 DIP

This basic Arm Cortex-M0+ NXP LPC810 breakout board features a FTDI programming header, USB (mini-B) connector for power only, a LM117-3.3v voltage regulator, power LED, ISP and Reset buttons and a standard 2×5-pin 0.05″ SWD debug connector. All pins are brought out to the edge of the board. Three PCBs from OSHPark.com cost me $6.55 USD delivered via the USPS. Eagle board package and BOM can be downloaded below.

Breakout board running a LED blinky program while powered via USB:
LPC810 BoB v1.00

Eagle Schematic:
LPC810s

Eagle Board:
LPC810BoB

Download the Eagle schematic and board file here.

LPC810BoB BOM
#  description            part#           package
1  microcontroller        LPC810M021JN8   DIP8
2  voltage regulator      LM117-N-3.3     SOT223
3  usb mini-b connector   UX60SC-MB-5ST   ---
4  cortex debug connector 3220-10-0100-00 ---
5  (2) 10uF ceramic cap   ---             0805
6  (2) 10k resistor       ---             0805
7  470 resistor           ---             0805
8  LED                    ---             1206
9  (2) smt tactile switch 147873-2        SMD
10 (3) pin header         ---             1x4, 1x4, 1x6
11 (optional) socket      ---             DIP8

Simple LED Blink program (compiled to 701 bytes):

//----------------------------------------------------------------------------
// Name:    LPC810_Blink.c
// Purpose: LED Flasher
// pin #8 set for LED
//----------------------------------------------------------------------------
#include <stdint.h>

//registers used for gpio & systick setup
#define GPIO_NOT             (*((volatile unsigned int *)(0xA0002300)))
#define GPIO_DIR             (*((volatile unsigned int *)(0xA0002000)))
#define SYSCON_PRESETCTRL    (*((volatile unsigned int *)(0x40048004)))
#define SYSCON_SYSAHBCLKCTRL (*((volatile unsigned int *)(0x40048080)))
#define NVIC_ST_CTRL         (*((volatile unsigned int *)(0xE000E010)))
#define NVIC_ST_RELOAD       (*((volatile unsigned int *)(0xE000E014))) 
#define NVIC_ST_CURRENT      (*((volatile unsigned int *)(0xE000E018))) 
#define SCB_SHP1             (*((volatile unsigned int *)(0xE000ED20)))

//our systick interrupt handler
void SysTick_Handler(void) {
  GPIO_NOT = (1<<0); //toggle led pin
}

int main(void) {
  //enable AHB clock to the GPIO domain
  SYSCON_SYSAHBCLKCTRL |= (1<<6);
  //reset gpio peripheral control
  SYSCON_PRESETCTRL &= ~(1<<10);
  SYSCON_PRESETCTRL |= (1<<10);
  //make gpio #0 pin an output (physical pin #8)
  GPIO_DIR |= (1<<0);

  //set systick clock interrupt reload count
  NVIC_ST_RELOAD = (1200000UL & 0xFFFFFFUL) - 1;
  //set SysTick IRQ to lowest priority
  SCB_SHP1 = 0xC0000000;
  //reset the counter
  NVIC_ST_CURRENT = 0;
  //enable the IRQ and go
  NVIC_ST_CTRL = 0x07;

  //loop forever
  while(1)
    asm("wfi"); //wait for interrupt
}
Posted in Uncategorized | Tagged , , , , , , , , , , | Leave a comment

LPC812 Switch Matrix

train tracks

One unique feature of the LPC8xx µC is the ability to swap pin functions. Want an UART TX on pin #10 and RX on #2? No problem. You can assign the pin functions via the Switch Matrix. This allows great flexibility when performing PCB layout. The Switch Matrix allows the flexible selection of the following peripheral pins:

USART0/1/2:

  • TX
  • RX
  • RTS
  • CTS
  • CLK

SPI0/1:

  • SCK
  • MISO
  • MISO
  • SSEL

I2C:

  • SDA
  • SCL

SCTimer (0-4):

  • Input
  • Output

Analog Comparator Output
Clock Out

Additionally, the Switch Matrix allows for turning fixed-pin functionality off if not needed. This frees up pins for alternate functionality. The following functions can be selected:

  • ACMP
  • External Crystal in/out
  • SWD
  • Reset
  • Clock in/out
  • VDDCMP

NXP produces both an online and downloadable version of LPC Initializer and Pin-muxing Tool. These are great tools for learning about and using the LPC Switch Matrix and I/O functions. There is even a video tutorial about using the tool.

Here is the code I used to inactivate the pull-up/down functions on PIO0s 8 and 9, then turn on the external crystal functionality (XTALIN and XTALOUT). The code then enables CLKOUT functionality on PIO01. This allowed me to connect a 20MHz crystal to the LPC812 to feed the PLL, and measure the clock frequency via the clock-out pin. Cool!

  //enable clock to SWM peripheral
  SYSCON_SYSAHBCLKCTRL |= (1<<SWM);

  //disable pull-up/down on pio08/9
  reg = IOCON_PIO0_8 & ~(0x3<<3);
  IOCON_PIO0_8 = reg | (0x0<<3);
  reg = IOCON_PIO0_9 & ~(0x3<<3);
  IOCON_PIO0_9 = reg | (0x0<<3);
  //enable XTALIN & XTALOUT on PIO08 & PIO09
  PINENABLE0 &= ~((1<<(uint32_t)4) | (~0x1ff));
  PINENABLE0 &= ~((1<<(uint32_t)5) | (~0x1ff));

  //feed PLL from external crystal bypassing sys osc 
  SYSCON_SYSOSCCTRL |= 1;
  //configure PIO01 with pull-up 
  reg = IOCON_PIO0_8 & ~(0x3<<3);
  IOCON_PIO0_8 = reg | (0x0<<3);
  //assign CLKOUT to PIO0_1 
  reg = PINASSIGN8 & (~(0xff<<0x10));
  PINASSIGN8 = reg | (0x01<<0x10);
  /*
  IRC        0 //Internal oscillator for CLKOUT 
  SYSOSC     1 //System oscillator for CLKOUT 
  WDTOSC     2 //Watchdog oscillator for CLKOUT
  MAINSYSCLK 3 //Main system clock for CLKOUT 
  */
  //set CLKOUT source & divider
  SYSCON_CLKOUTSEL = (uint32_t)3;
  SYSCON_CLKOUTUEN = 0UL;
  SYSCON_CLKOUTUEN = 1UL;
  SYSCON_CLKOUTDIV = 1;

  //disable clock to SWM peripheral
  SYSCON_SYSAHBCLKCTRL &= ~(1<<SWM);
Posted in Uncategorized | Tagged , , , , , , , | Leave a comment