AVRマイコンのプログラム   ホームページに戻る

1 AVR忘記簿

 AVRマイコンでアセンブラソースを書くときに気が付いたちょっとしたポイントです。

 (1)正負判定分岐命令

  レジスタ比較後に正負を判定し、分岐する場合は次のようで問題ないように思われますが落とし

  穴があります。 

   @sub Rd,Rr 、subi Rd,K 等

   Abrmi L1 負の場合(brpl L2 正の場合) 

  @の結果は2の補数で判断するので、正負判定はMSB(最上位ビット:0なら正・1なら負)で

  行うので、128(絶対値)以上なら負と判定してしまいます。

    レジスタ値    絶対値      2の補数

   $00〜$7F   0〜127    0〜127

   $80〜$FF  128〜255  −128〜−1

  2進法をよく理解している人には、当たり前のことですが理解の浅い私はよくはまり、ブランチマ

  イナス・ブランチプラス(brmi・brpl)の言葉の響きにだまされます。

   絶対値で大小比較するにはAを次のようにするのが正解です。

    brcs L1 負の場合(brcc L2 正の場合) 

     キャリー(Carry)有無(Set、clear)で正負を判定

     キャリー:演算結果が上位の桁に影響する場合Setされる。

       (繰り上がり、借りてくる)

   なお、inc・dec命令ではキャリーは発生しません。

 

 (2)16ビットレジスタアクセス

  AVRは8ビットバスなので、16ビットのレジスタ(タイマカウンタ、ADC用など)をアクセスする場合

  は、2回に分けて行うが読み込みと・書き込みでは手順が異なるので注意が必要。

  16ビットレジスタは下位バイトのアクセスによって読み書き操作が実行されるため(上位バイトは一時

  レジスタを介しアクセス)書込みは上位バイトから、読込みは下位バイトから連続して実施しなければ

  ならない。

    タイマカウンタ1の例

   16BIT_REG.GIF - 1,202BYTES

    書込手順

    @out TCTN1H,R17 ・・・上位バイトを一時レジスタ(temp)に設定

    Aout TCTN1L,R16 ・・・下位バイトをTCTN1Lに設定、temp(上位バイト)をTCTN1Hに設定

    読込手順

    @in R16,TCTN1L ・・・TCTN1Lから下位バイトを取得、TCTN1Hからtempに上位バイトを読込

    Ain R17,TCTN1H ・・・tempから上位バイトを取得

   読み書きとも、連続操作で実施しなければ値が不定となる恐れがある。上位と下位操作の間に割込み

    処理が発生する場合には、割込を無効にする(cli:全割り込み禁止)などの対応が必要。

 (3)RAM・ROMの使用

  変数や定数を使用するときSRAMやフラッシュメモリ(プログラムメモリ、以下PROM)を使いますが、

  このときの要点です。(EEPROMの読み書き方法は異なる、まだ使ったことがない)

  SRAM、PROMともラベルを設定し、後で間接アドレス指定レジスタ(X−Z、PROMはZのみ)

  を使い、読み書き(PROMは読み出しのみ)をします。

   アセンブラソース例

    .dseg          ;データ領域開始宣言  
      S_data:  .BYTE   10    ;BYTE擬似命令

                 SRAMにラベルS_dataを先頭に10バイト確保 (値は不定)


          .cseg          ;プログラム領域開始宣言 以降プログラムソースやPROM定数確保命令

     P_data:  .db    9,0x0A,$0b,0b00001100 ;db擬似命令 

                 ;PROM定数としてラベルP_dataを先頭に4バイト設定(10進で9,10,11,12)

     ldi  XL,low(S_data)   ;Xレジスタ(XH:XL=R27:R26)にS_dataのアドレス設定

          ldi  XH,high(S_data)  ;low(add)はadd(定数式)の下位値、high(add)はadd(定数式)の上位値

     st X,R16        ;(X)←R16  Xレジスタが示すアドレス(ラベルS_data番地)にR16の値代入                           

     ld r17,X+       ;R17←(X),X←X+1 R17ヘXレジスタが示すアドレスのデータ代入、その後

                 Xレジスタを1つ進める。この場合XレジスタはS_data+1番地となる

     ldi  ZL,low(P_data*2)  ;Zレジスタ(ZH:ZL=R31:R30)にP_dataのアドレス設定

     ldi  ZL,high(P_data*2) ;ラベル*2とするのがSRAMと違う

     lpm          ;RO←(Z) (R0は暗黙指定)

     lpm r16,Z+      ;R16←(Z)、Z←Z+1

   プログラムメモリは16ビット幅(ワード単位)のため、ラベル定数を指定するとき2倍する必要がある。(SRAMはラベルのまま)

   また、db擬似命令で定数を指定する場合コンマで区切り複数指定できるが、その数は偶数とする。奇数とするとワード

   単位で確保されるため、最後に'0'が追加される。dbを何行かに分けて記述する場合メモリのロスがでたり、先頭のラベ

   ルを基準にアクセスする場合、アドレス計算が合わなくなる。(ビルド時'0'を追加したメッセージで警告する) 

2 プログラムを書く中でよく使うマクロ・ルーチンの紹介

 数値演算関係:一般的なアルゴリズムを使用してます

 acroasm(マクロ)

  16bit×16bit符号無乗算

       mul16                ;mul16 ra,rb,rc,rd  ;r0:r1:r2:r3=ra:rb*rc:rd

                              ;work                r4

  16bit×16bit符号付乗算

        mul16s           ;mul16s ra,rb,rc,rd  ;r0:r1:r2:r3=ra:rb*rc:rd

                         ;work         r4,r28

16bit/16bit符号無除算

     div16              ;div16 ra,rb,rc,rd  ;ra:rb/rc:rd=r0:r1...r2:r3

div40_24.asm

 40bit/24bit 符号除算

   divu       ;40bit/24bit   [r4:r0]/[r7:r5]=r4:r0 

             ;if r7:r5=0 then r4:r7=9999999

 

 sqrt32.asm

32bit平方根 

筆算で平方根を解く手順をそのまま、プログラムしたもの    

sqrt32             ; sqrt(r16:r17:r18:r19)=r0:r1

                               ; work      r2-r8,r20-r22