円周率の計算 (13) -- BBP 公式による計算 (2)
今回は AVRStudio で ATtiny13A 用にコンパイルするソースファイル一式を掲載します。
使用したのは
の組み合わせで、他の環境では試していません。
まず、AVRStudio で ATtiny13A 用の C プロジェクトを立ち上げ、最適化レベルは「-Os」(サイズ優先) に設定し、下記のファイルをメインの C ソースファイルとしてコンパイル対象となるようにプロジェクトに追加します。
「pi_BBP.c」
/*************************************************/ /* pi_BBP.c : Pi calculation by BBP algorithm */ /* up to 4096 hexadecimal digits */ /* using integer arithmetics only */ /* */ /* based on "piqpr8.c" (2006-09-08) */ /* wrote by David H. Bailey */ /* */ /* 2012/10/18 Created by pcm1723 */ /*************************************************/ /* for ATtiny13A */ /*************************************************/ #define USE_AVR (1) #define USE_SUART (1) #define USE_ASM (1) #define PRINTLN xmit_nl // internal clock calibration flag //#define CLK_CALIB (1) #include <stdint.h> #include "suart.h" // BBP algorithm core #include "pi_BBP.pde" #include <avr/io.h> // transmit 2 hex digits void xmit_hex2(uint8_t d) { xmit_hex1(d >> 4); xmit_hex1(d); } // void xmit_hex2() // transmit 4 hex digits and a space void xmit_hex4s(uint16_t d) { xmit_hex2(d >> 8); xmit_hex2(d); xmit(' '); } // void xmit_hex4s() void main(void) { PORTB |= _BV(PORTB1); // hold TxD (PB1/MISO) "High" for idle DDRB |= _BV(DDB1); // set output direction for PB1/MISO #if !defined(CLK_CALIB) xmit_nl(); xmit('3'); // integer part xmit('.'); setup(); // calculate pi xmit_nl(); #else // internal clock oscillator calibration // typical (38.4 / 2) = 19.2 [kHz] square wave output from PB1/MISO/OC0B (pin 6) TCCR0A = (_BV(COM0B0) | _BV(WGM01)); // OC0B toggle, CTC mode TCCR0B = (_BV(CS00)); // CTC mode, 1/1 CLK OCR0A = 250 - 1; // 9.6 [MHz] / 250 = 38.4 [kHz] OCR0B = 0; #endif }
そして、下の 2 つのアセンブラのソース・ファイル (サフィックスが「.S」) も同じフォルダに置き、これもアセンブル対象となるようにプロジェクトに追加します。
「suart.S」
; ; Modified for CLK = 9.6 MHz (ATtiny13A) ; and omit rcvr() func ; and add xmit_nl(), xmit_hex1() func ; ; 2012/10/16 pcm1723 ; ;---------------------------------------------------------------------------; ; Software implemented UART module ; ; (C)ChaN, 2005 (http://elm-chan.org/) ; ;---------------------------------------------------------------------------; ; Bit rate settings: ; ; 1MHz 2MHz 4MHz 6MHz 8MHz 9.6MHz 10MHz 12MHz 16MHz 20MHz ; 2.4kbps 138 - - - - - - - - - ; 4.8kbps 68 138 - - - - - - - - ; 9.6kbps 33 68 138 208 - - - - - - ; 19.2kbps - 33 68 102 138 166 173 208 - - ; 38.4kbps - - 33 50 68 82 85 102 138 172 ; 57.6kbps - - 21 33 44 54 56 68 91 114 ; 115.2kbps - - - - 21 26 27 33 44 56 .nolist #include <avr/io.h> .list #define BPS 82 /* Bit delay. for 38.4 kbps, clk = 9.6 MHz, (see above table) */ ;#define BPS 77 /* Bit delay. for 38.4 kbps, clk = 9.06 MHz (-5.6%) */ #define BIDIR 0 /* 0:Separated Tx/Rx, 1:Shared Tx/Rx */ ; for ATtiny13A (8-PDIP) ; ; PB1/MISO (pin 6) for TxD ; PB0/MOSI (pin 5) for RxD #define OUT_1 sbi _SFR_IO_ADDR(PORTB), 1 /* Output 1 */ #define OUT_0 cbi _SFR_IO_ADDR(PORTB), 1 /* Output 0 */ #define SKIP_IN_1 sbic _SFR_IO_ADDR(PINB), 0 /* Skip if 1 */ #define SKIP_IN_0 sbis _SFR_IO_ADDR(PINB), 0 /* Skip if 0 */ #ifdef SPM_PAGESIZE .macro _LPMI reg lpm \reg, Z+ .endm .macro _MOVW dh,dl, sh,sl movw \dl, \sl .endm #else .macro _LPMI reg lpm mov \reg, r0 adiw ZL, 1 .endm .macro _MOVW dh,dl, sh,sl mov \dl, \sl mov \dh, \sh .endm #endif ;---------------------------------------------------------------------------; ; ; Prototype: void xmit_nl( void ); .global xmit_nl .func xmit_nl xmit_nl: ldi r24,0x0D ; CR rcall xmit ; transmit ldi r24,0x0A ; LF rjmp xmit ; transmit .endfunc ;---------------------------------------------------------------------------; ; Transmit a hex digit in lower nibble of R24 ; ; Prototype : ; ; void xmit_hex1(uint8_t d) { ; d &= 0x0F; ; xmit( (9 < d) ? ('A' + d - 10) : ('0' + d)); ; } ; .global xmit_hex1 .func xmit_hex1 xmit_hex1: andi r24, 0x0F ; extract lower nibble cpi r24, 10 ; 10 brcs 1f ; branch if (10 > r24) subi r24, -('A' - '0' - 10) 1: subi r24, -('0') .endfunc ; ; fall into xmit() ; ;---------------------------------------------------------------------------; ; Transmit a byte in serial format of N81 ; ;Prototype: void xmit (uint8_t data); ;Size: 16 words .global xmit .func xmit xmit: #if BIDIR ldi r23, BPS-1 ;Pre-idle time for bidirectional data line 5: dec r23 ; brne 5b ;/ #endif in r0, _SFR_IO_ADDR(SREG) ;Save flags com r24 ;C = start bit ldi r25, 10 ;Bit counter cli ;Start critical section 1: ldi r23, BPS-1 ;----- Bit transferring loop 2: dec r23 ;Wait for a bit time brne 2b ;/ brcs 3f ;MISO = bit to be sent OUT_1 ; 3: brcc 4f ; OUT_0 ;/ 4: lsr r24 ;Get next bit into C dec r25 ;All bits sent? brne 1b ; no, coutinue out _SFR_IO_ADDR(SREG), r0 ;End of critical section ret .endfunc #if (0) ;---------------------------------------------------------------------------; ; Receive a byte ; ;Prototype: uint8_t rcvr (void); ;Size: 19 words .global rcvr .func rcvr rcvr: in r0, _SFR_IO_ADDR(SREG) ;Save flags ldi r24, 0x80 ;Receiving shift reg cli ;Start critical section 1: SKIP_IN_1 ;Wait for idle rjmp 1b 2: SKIP_IN_0 ;Wait for start bit rjmp 2b ldi r25, BPS/2 ;Wait for half bit time 3: dec r25 brne 3b 4: ldi r25, BPS ;----- Bit receiving loop 5: dec r25 ;Wait for a bit time brne 5b ;/ lsr r24 ;Next bit SKIP_IN_0 ;Get a data bit into r24.7 ori r24, 0x80 brcc 4b ;All bits received? no, continue out _SFR_IO_ADDR(SREG), r0 ;End of critical section ret .endfunc #endif
「mulmod.S」
.nolist #include <avr/io.h> .list ;---------------------------------------------------------------------------; ; ; Prototype : ; ; uint16_t mulmod(uint16_t a, uint16_t b, uint16_t ak); ; argument: R25:R24, R23:R22, R21:R20 ; ; function result: R25:R24 ; ; local variable : R19:R18 ; .global mulmod .func mulmod mulmod: 1: ; while (a >= ak) { rjmp 3f ; 2: ; a -= ak; sub r24, r20 sbc r25, r21 ; a = a - ak 3: ; } // while(a >= ak) { ... cp r24, r20 cpc r25, r21 ; a - ak brsh 2b ; branch if SAME or HIGHER ; r = 0; ldi r18, 0x00 ; ldi r19, 0x00 ; r = 0 ; while (b) { rjmp 7f ; 4: ; if (0x01 & b) { sbrs r22, 0 ; skip if (1 == b0) rjmp 5f ; jump if (0 == b0) ; r += a; add r18, r24 adc r19, r25 ; r = r + a brcs 45f ; branch if carry occurred ; if (r >= ak) { cp r18, r20 ; cpc r19, r21 ; r - ak brlo 5f ; branch if LOWER ; r -= ak; 45: sub r18, r20 sbc r19, r21 ; r = r - ak 5: ; } // if (r >= ak) ... ; } // if (0x01 & b) ... ; a <<= 1; lsl r24 rol r25 brcs 55f ; branch if carry occurred ; if (a >= ak) { cp r24, r20 cpc r25, r21 ; a - ak brlo 6f ; branch if LOWER ; a -= ak; 55: sub r24, r20 sbc r25, r21 ; a = a - ak 6: ; } // if (a >= ak) ... ; b >>= 1; lsr r23 ror r22 ; b = b / 2 7: ; } // while (b) { ... cp r22, r1 cpc r23, r1 ; b - 0 brne 4b ; branch if (0 != b) ; return(r); movw r24, r18 ret .endfunc
また、下のヘッダ・ファイルも同じソースフォルダに置きます。
これはヘッダ・ファイルなのでプロジェクトのコンパイル対象として登録する必要はありません。
「suart.h」
#ifndef SUART #define SUART void xmit(uint8_t); uint8_t rcvr(); // transmit newline (CR + LF) void xmit_nl( void ); // transmit 1 hex digit void xmit_hex1(uint8_t c); // transmit 4 hex digits and a space void xmit_hex4s(uint16_t d); #endif /* SUART */
さらに、前回の「pi_BBP.pde」も同じフォルダに置きます。
これもインクルードして使われるので登録する必要はありません。
「suart.S」、「suart.h」は ChaN さんの web サイトのページ
ELM - AVR ライタの製作(4種)
の一番下の方にある
「ISP通信サンプル、汎用AVRモニタ(GCC)」
のリンクからダウンロードできるソフトウェア UART プログラムに手を加えたものです。
- 受信プログラムをオミット
- CPU クロック 9.6 MHz に対応
- CR/LF 出力、16 進 1 桁出力ルーチンの追加
等の変更が加えてあります。
「pi_BBP.c」の
#define CLK_CALIB (1)
の部分のコメントを外して「CLK_CALIB」が定義されるようにしてコンパイルしたオブジェクトを書き込んで動作させると、内蔵カウンタにより CPU クロックを 500 分周してシリアル出力 (6 番ピン) から出力するモードになります。
内蔵クロック 9.6 MHz を 500 分周すると 19.2 kHz の方形波となり、これをシリアル出力として見ると、38.4 kbps で ASCII 文字 'U' (0x55) を連続的に送信しているのと等価になります。
6 番ピンから出力される信号の周波数を測定して 500 倍すれば、CPU クロックの周波数を推定できます。
本来は、OSCCAL レジスタに書き込む値を調整して、内蔵クロック発振周波数自体を 9.6 MHz に近づけるのがスジですが、プログラム的に余裕がないので、「suart.S」内の
#define BPS 82 /* Bit delay. for 38.4 kbps, clk = 9.6 MHz, (see above table) */ ;#define BPS 77 /* Bit delay. for 38.4 kbps, clk = 9.06 MHz (-5.6%) */
のシンボル「BPS」の定数値そのものを変更します。
上でコメントアウトしてある 2 行目は手許にある ATtiny13A の Vcc = 3.3 V 時の実測値で、5.6 % ほど周波数が低かったので BPS の定数値も同じ割合で小さくしました。