たまりば

パソコン・インターネット パソコン・インターネット三鷹市 三鷹市

PIC10F200 OSCCAL調整用コード
2014年02月12日 00:29

PICの周波数調整用コードを書いた。
軽く説明しておくと、PICには(大抵)内蔵発振器があり、その周波数は(物によって)OSCCALレジスタの設定値で128段階などに微調整出来る(する必要があると言うべきか)。

周波数調整で問題なのが何を基準に合わせるかだが、今回はシリアル通信を使うことにした。
シリアル通信のボーレートを自動調整する事がよくあるが、それを逆に利用することで、クロック周波数が分かる。

動作のアルゴリズムは単純で、"?"の文字を受信するとそのパルス幅を数え、その数値を送信するだけである。それにボーレートを掛ければクロック周波数が分かるという仕組みだ。
なぜ"?"なのかだが、これは世のボーレート自動設定機能も大抵"?"を使っているのだが、実は作ってみるまで理由が分かっていなかった。
"?"のASCIIコードは0x3Fで、シリアルではスタート・ストップビットが付き、最下位bitから送信するので、0111111001となる。
この1が6連続と長く並んでいるので、この部分の長さを計って6で割るのだろうと最初は思っていたのだが、6は切りが悪いし、"@"なら0000000101と0が7つ並ぶのでこちらのほうが長い。また、0→1と1→0の遷移で時間が同じ保証がない。
ではどこを見るかというと、0→1の遷移が最初と最後にあるが、この間がちょうど8ビット時間と切りが良い。さらにその間に余計な0→1の遷移がない。
この条件を満たすには下位bitが1・上位bitが0でその間に切り替わりが1回のみである必要があるので、0x7F、0x3F、0x1F、0x0F、0x07、0x03、0x01のいずれかとなり、そのうち印字可能文字は"?"のみである。
世のボーレート自動設定のコードを見たことはないが、おそらくこれが"?"を使っている理由なのではないかと思っている。

概要を説明する。
まず使うPICは10F200である。これは最も機能の少ない石なので、プログラムを直して他の石で動くようにするのは簡単だろう。
ボーレートは300とした。これは10F200のタイマーで計れる最長時間がプリスケーラーを使って65536サイクルなので、8ビット時間がこれ以下になる値ということで決めた気がするが、実際の計り方を考えるとこれはあまり意味がなかった。まあいいや。
65536サイクルを計るわけだが、計数精度が1バイトでは心もとない。しかし2バイトで数えようとすると繰り上げ処理に時間を食ったりで途端に大変になるのでどうしようかと考えた結果、面白い方法を思いついた(のでブログに書きたくなった)。
アイディアの骨子はこうだ。
・まずオーバーフローしない程度の低分解能で大体の時間を計る
・もう一度今度は高分解能でオーバーフローを気にせず計る
・それを統合する
ここで上位バイトと下位バイトは1bit分重ねておく必要がある。そうでないと2回の誤差でちょうど繰り上がった時に判断ができない。

実際のコードでは、8ビット時間分の計測対象の前にちょうど1ビット時間分のスタートビットがあるので、これを低分解能で計っている。
あとは1回毎にOSCCALの設定値を2づつ(最小単位が2)変えて、計測結果とその時のOSCCALの値をシリアルで送信…しようと思ったが面倒なので1つ前のOSCCALの値とその時の結果を送信している。
シリアル送信のボーレートをきちんと合わせるのも面倒なので、1つ前の、しかも低分解能の計測結果を使っている。
なので正常に通信できないことを考えて、クロックが最大11%ずれていても送信できるモードを考えた。
・16進数を送信したいだけなので、下位4bitが正常に送れれば良い。
・しかし、ストップビットが正しくないと受け付けられない。
・であれば、上位bitをストップビットと同じ"1"にしておけば良い。
・16進数の0-Fを0xC0-0xCFとして送ることにしよう。
・0xC0は「タ」、0xCFは「マ」である。
・これをタマモードと名付けよう。
しかしなんか普通に送れたので使う機会がなかった。一応コードには残してあるが未チェックである。使うならストップビットの時間も長くしたほうがいい。

さてそれで計った生データがこちら。
1667D014678612663E1065F90E65B30C647B0A64350863F00663A904625E0262190061D4FE618EFC6079FA6035F85FF0F65FAAF45E62F25E1BF05DD6EE5D90EC5C5AEA5C13E85BD0E65B8BE45A40E259FBE059B7DE5872DC5823DA57DFD8579CD65658D4560FD255CAD05586CE5441CC540ACA53C6C85383C6523FC451F6C251B2C0506FBE502BBC4FB9BA4E76B84E32B64DEFB44DA8B24C63B04C23AE4BDEAC4BA8AA4A64A84A24A649E0A4499AA24858A048169E47D29C47849A46449846029645C09444799244399043F58E43B58C427D8A423D8841FB8641BA844072824030803FF0823FAF843FEE8640308840728A41BA8C41FB8E423D90427D9243B59443F79644379844799A45C09C46029E4644A04786A247D4A44814A64858A8499AAA49E2AC4A24AE4A66B04BA8B24BDFB44C21B64C65B84DA7BA4DEFBC4E32BE4E76C04FB8C2502BC4506FC651B2C851F6CA523FCC5383CE53C6D0540AD25441D45586D655CAD8560FDA5658DC579CDE57E1E05824E25871E459B7E659FCE85A41EA5B89EC5BCEEE5C15F05C5AF25D90F45DD6F65E1BF85E60FA5FAAFC5FEFFE603400607A02618F0461D30662190862600A63A90C63F00E643610647B1265B41465F916663E1867861A67D01C68161E685D2069A32269F1246A39266A7E286BC52A6C112C6C582E6D9E306DE5326E1E346E66366FAB386FF33A703C3C71843E71CB4072134273884473D046741648745E4A75AB4C75F34E76385077805277BA5478015678495879915A79DD5C7A255E7A6D607BB4627C03647C4B667D93687DDB6A7E286C7E706E7FB6707FFD72803674807E7681C478820B7A82577C839E7E83E47C842B7A83E478839E76825774820B7281C470807E6E80366C7FFD6A7FB5687E6E667E28647DDB627D93607C4B5E7C055C7BB45A7A6D587A255679DC5479915278495078024E77BA4C77814A763B4875F34675AB44745E4274164073D03E73883C72133A71CC38718436703C346FF1326FAB306E642E6E1E2C6DE62A6D9E286C58266C11246BC6226A7E206A381E69F11C69A31A685B1868161667D01467861266401065F90E65B30C647A0A64350863F00663A904625E0262190061D4FE618EFC6079FA6035F85FEFF65FAAF45E60F25E1BF05DD6EE5D91EC5C5AEA5C15E85BD0E65B8BE45A41E259FCE059B7DE5872DC5824DA57DFD8579CD65658D4560FD255CAD05586CE5441CC540ACA53C6C85383C6523FC451F7C251B2C0506FBE502BBC4FB9BA4E76B84E32B64DEFB44DA8B24C66B04C23AE4BDFAC4BA9AA4A67A84A24A649E2A4499AA24858A048169E47D59C47879A46459846029645C1

グラフにしたのがこちらである。
PIC10F200 OSCCAL設定値と周波数
これを見ると面白いのが、32個ごと(値は64ごと)に不連続な点が見える。折角なので差分もグラフにしてみると、16個ごとや8個ごとにも値が飛んでいるのが分かる。
たぶん、周波数の微調整のためにはCR発振器のCやRを変化させるために抵抗ラダーのような構成にしているのだろうが、その特性のずれが見えているのだろう。

コードは以下のとおり。無保証あずいず。
シリアルポートとの結線はこちらを参考にしました。
RS-232C - TTLレベルの簡易変換方法 (ELM by ChaN)
    list p=10F200
    #include p10f200.inc
    radix dec

    ;    N/C+-v-+(GP3)
    ;    Vdd|   |Vss
    ;    GP2|   |N/C
    ;    GP1+---+GP0

    __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF

    cblock 0x10
        tmr32
        tmr2
        flag
        cnt
        waitcnt
        waitcnt2
        txdat
    endc

;レジスタ使いまわし
#define temp waitcnt
#define temp2 waitcnt2

;プリスケーラー設定用
#define OPTVAL2  b'10010000' ;2:1
#define OPTVAL32 b'10010100' ;32:1
#define OPTVAL64 b'10010101' ;64:1
;                  ^/GPWU: DontCare
;                   ^/GPPU DontCare
;                    ^T0CS=0: Fosc/4,GP2=I/O
;                     ^T0SE Don'tCare
;                      ^PSA TMR0
;                       ^^^PS

#define FCLK .4000000 ;クロック周波数
#define BAUD .300 ;ボーレート
#define ASCEND 0

#define TAMA 0 ;タマモード(未使用)

#define SERIAL_WIDTH (FCLK/4 / BAUD / 32) ;シリアルの1ビット時間(プリスケーラー32)

init:
    org 0x00
    
    movwf OSCCAL
    movlw SERIAL_WIDTH
    movwf tmr32

    clrf cnt
    
    clrw
    movwf GPIO
    tris GPIO

    bcf flag,ASCEND

mainloop:
    movlw OPTVAL64
    option ;ps=64

wait2bhigh: ;wait 2bit high pulse

    btfss GPIO,3 ; wait ^
    goto $-1
    clrf TMR0
    bcf STATUS,C
    rrf tmr32,W
    movwf temp
    bcf STATUS,C
    rrf temp,W
    subwf tmr32,W; W=tmr32*0.75
    btfsc GPIO,3 ; wait v
    goto $-1
    subwf TMR0,W
    btfss STATUS,C
    goto wait2bhigh

    movlw OPTVAL32
    option ;ps=32

count1bit:
    btfss GPIO,3 ;wait ^ [0,3)
    goto $-1
    clrf TMR0 ;[2,5)+2
    btfsc GPIO,3 ;wait v [0,3)
    goto $-1
    goto $+1
    movf TMR0,W ;[4,7)
    movwf tmr32

    movlw OPTVAL2 ;ps=2
    option
    clrf TMR0 ;[8,11)

count8bit:
    btfss GPIO,3 ; wait ^
    goto $-1
    btfsc GPIO,3 ; wait v [0,3)
    goto $-1
    nop
    movlw 2 ;(8-4)/2
    addwf TMR0,W ;[4,7)
    movwf tmr2
    
    movlw 2
    btfss flag,ASCEND
    movlw -2
    addwf OSCCAL,F

;方向転換    
    movlw 0x80
    subwf OSCCAL,W
    btfsc STATUS,Z
    bsf flag,ASCEND
    movlw 0x7E
    subwf OSCCAL,W
    btfsc STATUS,Z
    bcf flag,ASCEND

;カウント上位を補正
    rlf tmr2,W ;tmr2,7 -> C
    rlf 0x0F,W ;Cが欲しいだけ
    xorwf tmr32,W
    movwf temp
    btfss temp,0 ;if tmr2,7!=tmr32,0 then do
    goto skipadj
;補正実行
    btfsc tmr2,6
    decf tmr32
    btfss tmr2,6
    incf tmr32
skipadj:

;結果送信
    swapf OSCCAL,W
    call tx232
    movf OSCCAL,W
    call tx232
    swapf tmr32,W
    call tx232
    movf tmr32,W
    call tx232
    swapf tmr2,W
    call tx232
    movf tmr2,W
    call tx232

    goto mainloop

;シリアル送信
tx232:
    andlw 0x0F
    movwf txdat
#if TAMA
    movlw 0xC0
#else
    movlw 6
    addwf txdat,W
    movlw '0'
    btfsc STATUS,DC
    movlw 'A'-10
#endif
    bsf GPIO,2 ;#
    addwf txdat,F
    nop
    call wait32n
    bsf cnt,3
txloop:
    movf GPIO,W
    andlw 0x0B
    rrf txdat,F
    btfss STATUS,C ;極性反転
    iorlw 0x04
    movwf GPIO ;# >9, >9>
    call wait32n
    decfsz cnt,F
    goto txloop
    call wait6
    bcf GPIO,2 ;# >3
    call wait32n
    retlw 0

wait32n: ;wait 3+32n
    movf tmr32,W
    movwf waitcnt
waitloop:
    nop
    movlw .9
    movwf waitcnt2
waitloop2:
    decfsz waitcnt2,F
    goto waitloop2
    decfsz waitcnt,F
    goto waitloop
    retlw 0

wait6:
    goto $+1
    retlw 0

    end

  • 同じカテゴリー(プログラム・アルゴリズム)の記事画像
    6段のカレンダーが好きだ
    ファミコンで9×9ドット文字表示(ほか)
    JPEG圧縮を繰り返しても際限なく劣化するわけではない
    ゲームボーイの吸い出し機を作った (後編)
    ファミコンで全画面に任意の画像(ただしモノクロ)を表示
    最近のWindowsのビットマップフォントの太字
    同じカテゴリー(プログラム・アルゴリズム)の記事
     CDのピットの数 (2021-03-05 23:32)
     6段のカレンダーが好きだ (2020-11-15 04:06)
     PIC16のDhrystone MIPSを測ろうとしてみた (2020-04-29 02:23)
     Twitterの画像の扱いがやっとまともになって嬉しい (2019-06-23 00:23)
     ファミコンで9×9ドット文字表示(ほか) (2019-04-08 02:02)
     謎の色名「honeydewtab」とlegacy color valueパース手順 (2018-03-24 12:45)
    URL欄を実験的に消してる間に廃止されてしまいました。まあいいか。
     
    <ご注意>
    書き込まれた内容は公開され、ブログの持ち主だけが削除できます。
    削除
    PIC10F200 OSCCAL調整用コード
      コメント(0)