LPC1114FN28/102 (7) -- 円周率の計算 (11)

LPC1114FN28/102 で Spigot アルゴリズムを使って円周率Πの値 500 桁を UART 経由でシリアル回線に出力するプログラムを作りました。
プログラムの大きさは、フラッシュに書き込むサイズとして 0x47c = 1148 バイトです。
ターミナル・ソフト (tt.exe) でキャプチャした出力を下に示します。

LPC1114FN28/102
Spigot 16 bit (nterms = 1758)

pi = 3.

14159265358979323846
26433832795028841971
69399375105820974944
59230781640628620899
86280348253421170679

82148086513282306647
09384460955058223172
53594081284811174502
84102701938521105559
64462294895493038196

44288109756659334461
28475648233786783165
27120190914564856692
34603486104543266482
13393607260249141273

72458700660631558817
48815209209628292540
91715364367892590360
01133053054882046652
13841469519415116094

33057270365759591953
09218611738193261179
31051185480744623799
62749567351885752724
89122793818301194912

オブジェクトの HEX ファイルを下に示します。
Flash Magic」などで書き込めば実行できます。
シリアル回線のスピードは 115.2 kbps です。
(2012/10/11 追記: 当初、外部水晶振動子が必要なバージョンを掲載していましたが、内部 RC オシレータ (IRC) で動くバージョンに差し替えました。)

:020000040000FA
:10000000E00F0010D5000000DD000000DF00000060
:1000100000000000000000000000000000000000E0
:10002000000000000000000000000000E1000000EF
:100030000000000000000000E3000000E5000000F8
:10004000E7000000E7000000E7000000E700000014
:10005000E7000000E7000000E7000000E700000004
:10006000E7000000E7000000E7000000E7000000F4
:10007000E7000000E7000000E7000000E7000000E4
:10008000E7000000E7000000E7000000E7000000D4
:10009000E7000000E7000000E7000000E7000000C4
:1000A000E7000000E7000000E7000000E7000000B4
:1000B000E7000000E7000000E7000000E7000000A4
:1000C0000348854600F046F80048004779030000E1
:1000D000E00F00100448804704480047FEE7FEE7B1
:1000E000FEE7FEE7FEE7FEE701030000C1000000B7
:1000F00030B50B46014600202022012409E00D46C0
:10010000D5409D4205D31D469540491B2546954047
:1001100040191546521E002DF1DC30BD70B500248B
:100120002546002801DA01244042002901DA012590
:100130004942FFF7DDFFAC4200D04042002C00D026
:10014000494270BD10B5044608461146FFF7E6FF68
:1001500003C410BD064C0125064E05E02046E368A9
:1001600007C82B4398471034B442F7D3FFF7ACFFCE
:10017000D8020000E8020000F0B5074685B0012073
:10018000009002203A4D0190002042000199401C4D
:10019000B952A842F9DB37A000F05CF939A000F0B1
:1001A00059F939A000F056F901220021284600F043
:1001B00011F93CA000F04EF932A000F04BF938A044
:1001C00000F048F92FA000F045F937A000F042F9FF
:1001D00000216C1E022C10DB6600701E82B2B85B20
:1001E000334B61435843091802A8FFF7ABFF03984C
:1001F000B853641E0299022CEEDA2D4A02A8FFF7CA
:10020000A1FF02990198009A0818002100F0E2F875
:10021000042000900398164C80B20190FF21601BCF
:100220005F31FFF77BFF0E4605213046FFF776FF73
:10023000002912D1A54202D11EA000F00BF918A08E
:1002400000F008F90FA000F005F9002E05D114A068
:1002500000F000F90BA000F0FDF80E3D002DB7DC1A
:100260000FA000F0F7F807A000F0F4F805B0F0BD1B
:10027000DE0600004C504331313134464E32382FC7
:10028000313032000D0A0000537069676F742031FD
:10029000362062697420286E7465726D73203D206B
:1002A00000000000290000007069203D20000000CF
:1002B000102700002E00000002E008C8121F08C12D
:1002C000002AFAD170477047002001E001C1121FD7
:1002D000002AFBD1704700007C04000000000010E1
:1002E000E00F0000C8020000000000000000000055
:1002F000000000000000000000000000FFFFFFFF02
:1003000070B4184A906B20218843906316490023EB
:100310000B62184600BF401CC828FBD3134803607B
:1003200001254560436045604468E407FCD0232410
:100330008C60946B8026B4439463CA68D207FCD067
:1003400003210163456343634563416BC907FCD0E7
:10035000856308480649016045618561C56170BCD7
:100360007047000000820440008004404080044048
:100370005F0001008080044010B512490868012226
:100380001204104308601048F1224262C1228262C6
:10039000086801221203104308600C4801228A6198
:1003A000C72181608321C1601A2101600021416061
:1003B000C16880229143C1600548FFF7DDFE00203F
:1003C00010BD000080800440804004400080004058
:1003D00022000010F8B50446012021250E461746DC
:1003E0000090002905D1002C02DA00206442009020
:1003F0000A2617480021415531462046FFF778FE6E
:1004000002467043201A3721092800DC30210818E1
:100410006D1E0F491400485501D0012000E0002056
:100420007F1E002F01DD012100E000210843012192
:10043000012D00DC00210842DED105482D216D1E72
:10044000415500996918081800F004F8F8BD00003B
:1004500000000010017800290DD010B4064CCBB27A
:10046000401C21464A699206FCD50B6001780029A0
:0C047000F5D110BC7047704700800040C0
:04000005000000D522
:00000001FF

最後にソース・リストを示します。
Keil/ARM MDK-Lite uVision V4.20 を使って作成しました。
他の環境では試していません。

/*******************************************************/
/* pi_LPC11.c : Calculate pi 500 decimal digits        */
/*              by Spigot algorithm                    */
/*              for LPC1114FN28/102                    */
/*              using Keil/ARM MDK-lite uVision4 IDE   */
/*                                                     */
/* 2012/10/11 created by pcm1723                       */
/*******************************************************/
#include "lpc11xx.h"
#include <string.h>
#include <stdlib.h>
                                          
#define CPUCLK_Hz 48000000UL
#define PCLK_Hz CPUCLK_Hz

// UART divisor latch values for 115.2 kbps @ PCLK = 48 MHz
// actual bps = 115.38462 kbps (+0.16 %)

#define UART_UDL (26)

#define USE_LDIV (1)

#define PUT_CHAR(c)      LPC11xx_UART_putc(c)
#define PRINT_STR(s)     out_str(s)
#define PRINT_DEC(d)     out_radix((d),0,0)
#define PRINT_DEC_W(d,w) out_radix((d),0,(w))
#define PRINTLN_STR(s)   (PRINT_STR(s),PRINT_STR("\r\xa"))
#define PRINTLN_DEC(d)   (PRINT_DEC(d),PRINT_STR("\r\xa"))

#define IOCON_fields(func,mode,hys,admode) \
    ( ( 0x07 & func        ) \
    | ((0x03 & mode)   << 3) \
    | ((0x01 & hys)    << 5) \
    | (    1           << 6) \
    | ((0x01 & admode) << 7) \
    )

#define MP16LEN_MAX (1759)

// multi precision (MP) array of 16-bit numbers
typedef uint16_t mp16_t[MP16LEN_MAX];

mp16_t a1_16;

//
// LPC1114FN28/102 peripheral setup functions
//
void LPC11xx_IOCON_setup( void )
{
  register LPC_IOCON_TypeDef *p = LPC_IOCON;

//                           F  M  H  A
//                           U  O  Y  /
//                           N  D  S  D
//                           C  E
  p->PIO1_6   = IOCON_fields(1, 2, 1, 1);  // Pin16: RXD (pull-up)
  p->PIO1_7   = IOCON_fields(1, 0, 0, 1);  // Pin15: TXD
} // void LPC11xx_IOCON_setup()

void LPC11xx_SYSCON_setup( void )
{
  register LPC_SYSCON_TypeDef *p = LPC_SYSCON;

// at first, enable AHB clock for IOCON module
  p->SYSAHBCLKCTRL |= (1 << 16); // IOCON
// configure I/O pins for modules
  LPC11xx_IOCON_setup();
// enable AHB clock for modules
  p->SYSAHBCLKCTRL |= (  0
//                    | (1 <<  5) // I2C
//                    | (1 <<  7) // CT16B0
//                    | (1 <<  8) // CT16B1
//                    | (1 <<  9) // CT32B0
//                    | (1 << 10) // CT32B1
//                    | (1 << 11) // SSP0
                      | (1 << 12) // UART
//                    | (1 << 13) // ADC
//                    | (1 << 15) // WDT
                      );
// de-assert RESET for modules
//p->PRESETCTRL |= (1 << 0); // SSP0                           ;
} // void LPC11xx_SYSCON_setup()

void LPC11xx_UART_setup( void )
{
  register LPC_UART_TypeDef *p = LPC_UART;

  LPC_SYSCON->UARTCLKDIV = 1;  // UART clock = CCLK/1 = 48 MHz
// FIFO control
  p->FCR = (  0
           | (1 << 0) // enable FIFOs
           | (1 << 1) // reset RX FIFO
           | (1 << 2) // reset TX FIFO
           | (3 << 6) // set RX FIFO trigger = 14 char
           );
// data format control
  p->LCR = (  0
           | (3 << 0) // 8 bit word length
           | (0 << 2) // 1 stop bit
           | (0 << 3) // no parity bit
           | (0 << 4) // odd parity (ignored)
           | (0 << 6) // disable break TX
           | (1 << 7) // enable divisor latch access
           );
// set divisor latch for the bps
  p->DLL = (0xff & UART_UDL); // low  byte of divisor
  p->DLM = (UART_UDL >> 8);   // high byte of divisor
  p->LCR &= ~(1 << 7); // disable divisor latch access
} // void LPC11xx_UART_setup()

void LPC11xx_periph_setup( void )
{
  LPC11xx_SYSCON_setup();
  LPC11xx_UART_setup();
} // void LPC11xx_periph_setup()

//
// LPC1114 terminal output functions
//
void LPC11xx_UART_putc(int c)
{
  register LPC_UART_TypeDef *p = LPC_UART;
  while (0 == ((1 << 5) & p->LSR)) {} // wait until THR empty
  p->THR = c; // xmit the character
} // void LPC11xx_UART_putc()

void out_str(char* s)
{
  while (*s) {
    LPC11xx_UART_putc(*(s++));
  } //
} // void out_str()

//
// output int32_t argument 'd'
// with 'radix' notation
// in at least 'width' column
//
// (0 == radix) means signed decimal format
// otherwise unsigned radix format
//
void out_radix(int32_t d, int radix, int width)
{
  static char dig_buf[32+2]; // 32 binary digit + sign + null
  int d_plus = 1 ;
  int i = sizeof(dig_buf)-1;
  uint32_t quo;
  
  if (0 == radix) { // signed decimal
    if (0 > d) { // negative number
      d_plus = 0; // remember sign
      d = (-d);   // make positive
    } // if (0 > d) { ...
    radix = 10; // force decimal radix
  } // if (0 == radix) { ...
  dig_buf[i] = 0; // end of string
  do { 
    quo = (uint32_t) d / (uint32_t) radix;
    d  -= (quo * radix); // remainder
    dig_buf[--i] = (d + ((9 < d) ?  ('A' - 10) : '0'));
    d = quo; // quotient
  } while ((1 < i) & ((0 != d) | (0 < (--width))));
  dig_buf[--i] = '-'; // minus sign
  PRINT_STR(&dig_buf[i+d_plus]); // output
} // void out_radix()

//
// calculate pi by Spigot algorithm using 16-bit arithmetics
//
void Spigot_16b( mp16_t num ) // MP work 
{
  const    uint16_t  base   = 10000; // n-ary number
  const    uint16_t  log10_base = 4; // log10(base)
  const    uint16_t  nterms = MP16LEN_MAX-1; // number of terms
  const    uint16_t  n_step = 14;   // calculation interval
  const    uint16_t  digits_per_line  = 20;
  const    uint16_t  digits_per_block = 100;
  register  int32_t  i;     // loop index
  register  int32_t  n;     // loop index
  register uint32_t  temp;  // temporary work / carry
  register uint16_t  denom; // denominator of series
  register uint16_t  out;   // output digits / carry adjust
  auto      int32_t  width = 1; // column width for integer part
#if (USE_LDIV)
  ldiv_t quo_rem;
#endif 

  out = 2;
  for( i = 0 ; i < nterms ; i++ ) { 
    num[i] = out; // initalize array
  }
  PRINTLN_STR("LPC1114FN28/102");
  PRINT_STR  ("Spigot 16 bit (nterms = ");
  PRINT_DEC_W(nterms, 1);
  PRINTLN_STR(")"); 
  PRINTLN_STR(""); 
  PRINT_STR  ("pi = ");
  for( n = nterms ; n > 0; n -= n_step ) {
    temp = 0;
    for( i = n - 1 ; i >= 2 ; i-- ) {
      denom = 2 * i - 1;
      temp = temp * i + num[i] * base;
#if (USE_LDIV)
      quo_rem = ldiv(temp, denom);
      num[i]  = quo_rem.rem;
      temp    = quo_rem.quot;
#else      
      num[i] = (temp % denom); // remainder
      temp /= denom; // quotient
#endif
    } // for ( i = n - 1 ; ...
#if (USE_LDIV)
    quo_rem = ldiv(temp, base);
    PRINT_DEC_W((out + quo_rem.quot), width);
    width = 4; // output column width for fraction part
    out = quo_rem.rem;
#else    
    PRINT_DEC_W((out + temp / base), width);
    width = 4; // output column width for fraction part
    out = temp % base;
#endif    
// output formatting    
    i = ((nterms-n) % (n_step * (digits_per_block / log10_base)));
// put decimal point or newline
    if (0 == (i % (digits_per_line / log10_base))) { 
      if (nterms == n) {
        PRINT_STR("."); // decimal point
      } // if (nterms == n) ...
      PRINTLN_STR(""); // newline
      if (0 == i) { // separate block of digits
        PRINTLN_STR(""); // newline
      } // if (0 == i) { ...
    } // if (0 == (i %  ... 
  } // for ( n = nterms ; ...
  PRINTLN_STR("");
} // void Spigot_16b()

int main( void )
{
  LPC11xx_periph_setup();
  Spigot_16b(a1_16);
} // void main()