エンジニアの夏休み:蓄光テープで「省エネディスプレイ」を作る

第4回本体側の文字描画処理はどうなっているのか

蓄光テープに文字を描くライントレーサですが、マイコンの内部ではいろいろな処理を行っています。

  • LANのためのシリアル送受信
  • モータ2個のPWM処理
  • ライントレース処理

今回は、どのようにしてこれらの処理を行ったかを解説します。ぜひマイコンで制御を行う際の参考にしてください。

LED文字描画システム全体
LED文字描画システム全体

LANのためのシリアル送受信

まず重要な点ですが、今回はシリアル送受信をソフトウェアでおこないました。PICには内蔵シリアルもあるのですが、使えるピンが固定されています。今回は8bit連続で取りたかったので、シリアルをソフトウェアで処理するようにしました。

リスト1 シリアル送受信処理
waitrx1
    movlw    0x55            ; (87clk - 2)
    subwf    TMR0, 1
waitrx0
    btfsc    TMR0, 7
    goto    waitrx0
    
    movf    rxphase, 0
    addwf    rxphase, 0
    incf    rxphase, 1
    
    addwf    PCL, 1
    
        ; 0:start
    btfsc    PORTA, PORTA_RX
    clrf    rxphase
    
    retlw    0
    nop
    
    retlw    0
    nop
        ; 3:bit0
    clrf    rxwork
    retlw    0
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 0
    
    retlw    0
    nop
        ; 6:bit1
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 1
    
    retlw    0
    nop
        ; 9:bit2
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 2
    
    retlw    0
    nop
        ; 12:bit3
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 3
    
    retlw    0
    nop
        ; 15:bit4
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 4
    
    retlw    0
    nop
        ; 18:bit5
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 5
    
    retlw    0
    nop
        ; 21:bit6
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 6
    
    retlw    0
    nop
        ; 24:bit7
    retlw    0
    nop
    
    btfsc    PORTA, PORTA_RX
    bsf    rxwork, 7
    
    retlw    0
    nop
        ; 27:stop
    movf    rxwork, 0
    movwf    rxdata
    clrf    rxphase
    retlw    0

ソフトウェアで処理する場合、ビットレートの3倍、たとえば9600bpsなら28800bpsで、約35μsごとにタイマ処理をおこないます。この中で、スタートビットを検出してから4クロック後をビット0、7クロック後をビット1というように読み込みます。すると、他の処理を行いながらシリアルを受信することができます。

ここで注意しないといけないのが、マイコンのクロック数です。今回は20MHzを使ったので、1命令を0.2μsで実行できます。シリアル受信は35μs間隔ですから、この中で約175命令を実行できます。今回のコードでは、シリアル受信部分がcall/returnを含めて20クロック弱ですから、タイマ処理ごとにあと150クロックほど余裕があることになります。

モータのPWM制御

その空きクロックを使って、モータのPWM制御を行ってしまいます。PWM制御というのは、スイッチのオン・オフをすばやく切り替え、アナログ的な出力を得る方法です。今回は35μsのタイマ処理で、256のカウントをおこないます。カウントと設定値を足してオーバーフローしたら、スイッチを入れます。これにより、0だとオフ、255だとオン、それ以外ではその中間という出力が得られます。

なお、今回はステッピングモータを使用しましたので、n相とn+1相の比率をPWMで切り替えています。PWMの周波数は、28800bpsの1/256で、約110Hzになります。

リスト2 モータのPWM制御処理
    movf    posll, 0
    addwf    pwmval, 0
    movlw    0
    btfsc    STATUS, C
    movlw    1
    addwf    poslh, 0
    
    call    getmotor
    andlw    0xf0
    movwf    work0
    
    movf    posrl, 0
    addwf    pwmval, 0
    movlw    0
    btfsc    STATUS, C
    movlw    1
    addwf    posrh, 0
    
    call    getmotor
    andlw    0xf
    iorwf    work0, 0
    
    movwf    PORTC
    
    incf    pwmval, 1
    btfss    STATUS, Z
    goto    waitrx1
    
    movf    vell, 0
    addwf    posll, 1
    btfsc    STATUS, C
    incf    poslh, 1
    
    movf    velr, 0
    addwf    posrl, 1
    btfsc    STATUS, C
    incf    posrh, 1

次に、PWMのカウンタがラウンドしたところで、速度をステップ位置に加算します。これで、指定した速度でステッピングモータの相が進み、モータが回転するという仕組みです。モータの速度はライントレーサのセンサによって決めています。この処理は、もっと外側で行っています。

HTTPリクエストの生成

いちばん外側の処理は、サーバにリクエストを出してそのレスポンスを解析し、LEDを制御するという繰り返しです。LANモジュールには接続先のIPアドレスがセットしてあり、シリアルで文字を送ると、目的のサーバに自動的に接続します。ここに「GET / HTTP/1.0」のような文字列を入れておくことで、サーバにHTTPリクエストが送信されます。

リスト3 HTTPリクエスト生成
main
    call    traceproc
    
    movlw    0x47            ; 'G'
    call    sendrs
    movlw    0x45            ; 'E'
    call    sendrs
    movlw    0x54            ; 'T'
    call    sendrs
    movlw    0x20            ; ' '
    call    sendrs
    movlw    0x2f            ; '/'
    call    sendrs
    movlw    0x75            ; "u"
    call    sendrs
    movlw    0x76            ; "v"
    call    sendrs
    movlw    0x63            ; "c"
    call    sendrs
    movlw    0x61            ; "a"
    call    sendrs
    movlw    0x72            ; "r"
    call    sendrs
    
    call    traceproc
    
    movlw    0x2f            ; '/'
    call    sendrs
    movlw    0x3f            ; '?'
    call    sendrs
    movlw    0x6f            ; 'o'
    call    sendrs
    movlw    0x66            ; 'f'
    call    sendrs
    movlw    0x66            ; 'f'
    call    sendrs
    movlw    0x73            ; 's'
    call    sendrs
    movlw    0x65            ; 'e'
    call    sendrs
    movlw    0x74            ; 't'
    call    sendrs
    movlw    0x3d            ; '='
    call    sendrs
    movf    recvbuf, 0
    call    sendrs
    
    call    traceproc
    
    movf    recvbuf + 1, 0
    call    sendrs
    movf    recvbuf + 2, 0
    call    sendrs
    movf    recvbuf + 3, 0
    call    sendrs
    movf    recvbuf + 4, 0
    call    sendrs
    movf    recvbuf + 5, 0
    call    sendrs
    movf    recvbuf + 6, 0
    call    sendrs
    movf    recvbuf + 7, 0
    call    sendrs
    movlw    0x20            ; ' '
    call    sendrs
    movlw    0x48            ; 'H'
    call    sendrs
    movlw    0x54            ; 'T'
    call    sendrs
    
    call    traceproc
    
    movlw    0x54            ; 'T'
    call    sendrs
    movlw    0x50            ; 'P'
    call    sendrs
    movlw    0x2f            ; '/'
    call    sendrs
    movlw    0x31            ; '1'
    call    sendrs
    movlw    0x2e            ; '.'
    call    sendrs
    movlw    0x30            ; '0'
    call    sendrs
    movlw    0xd
    call    sendrs
    movlw    0xa
    call    sendrs
    movlw    0xd
    call    sendrs
    movlw    0xa
    call    sendrs
    
waitresp
    clrf    rxdata
waitresp0
    call    traceproc
    movf    rxdata, 0
    btfsc    STATUS, Z
    goto    waitresp0
    
    xorlw    0xd
    btfss    STATUS, Z
    goto    waitresp
    
    clrf    rxdata
waitresp1
    call    traceproc
    movf    rxdata, 0
    btfsc    STATUS, Z
    goto    waitresp1
    
    xorlw    0xa
    btfss    STATUS, Z
    goto    waitresp
    
    clrf    rxdata
waitresp2
    call    traceproc
    movf    rxdata, 0
    btfsc    STATUS, Z
    goto    waitresp2
    
    xorlw    0xd
    btfss    STATUS, Z
    goto    waitresp
    
    clrf    rxdata
waitresp3
    call    traceproc
    movf    rxdata, 0
    btfsc    STATUS, Z
    goto    waitresp3
    
    xorlw    0xa
    btfss    STATUS, Z
    goto    waitresp
    
    clrf    rxdata
    movlw    recvbuf
    movwf    bufpos
recvbody
    call    traceproc
    movf    rxdata, 0
    btfsc    STATUS, Z
    goto    recvbody
    
    andlw    0xf0
    xorlw    0x30
    btfsc    STATUS, Z
    goto    recvbody0
    xorlw    0x70
    btfsc    STATUS, Z
    goto    recvbody0
    xorlw    0x20
    btfss    STATUS, Z
    goto    putchar
recvbody0
    movf    bufpos, 0
    movwf    FSR
    movf    rxdata, 0
    movwf    INDF
    incf    bufpos, 1
    
    clrf    rxdata
    movf    bufpos, 0
    xorlw    recvbuf_end
    btfss    STATUS, Z
    goto    recvbody
putchar
    movlw    recvbuf + 8
    movwf    FSR
    movlw    4
    movwf    work2
putchar0
    call    traceproc
    btfsc    posrh, 0
    goto    putchar0
    
    movf    INDF, 0
    btfsc    INDF, 6
    addlw    9            ; 0x61 -> 0x6a
    andlw    0xf
    movwf    work1
    swapf    work1, 1
    
    incf    FSR, 1
    movf    INDF, 0
    btfsc    INDF, 6
    addlw    9            ; 0x61 -> 0x6a
    andlw    0xf
    iorwf    work1, 0
    xorlw    0xff
    movwf    PORTB
    
    incf    FSR, 1
putchar1
    call    traceproc
    btfss    posrh, 0
    goto    putchar1
    
    movlw    0xff
    movwf    PORTB
    
    decfsz    work2, 1
    goto    putchar0
    
    goto    main

サーバはHTTPリクエストを受け取ると、レスポンスを返します。マイコン側ではこれを受信してメモリに格納しておきます。そのあと、受信したデータを順次LEDに出力していきます。出力がおわったら、次の文字のHTTPリクエストを送信します。今回は内側のステッピングモータの回転に合わせてLED出力を変えるようにしました。これにより、蓄光テープを見失って180°回転しているところでは文字の描画がストップします。

全体としては、まずシリアルにリクエストを出力する処理の中で、シリアルの35μsタイミング待ちが発生します。ここでPWM処理が定期的に呼ばれます。そのあと、シリアルの受信待ちがありますが、ここでもタイミング待ちが定期的に呼ばれます。このように、何をやるにしてもループの中でシリアルのタイミング待ちを呼ぶことで、シリアル受信やPWM処理を途切れさせないようになっています。ちなみにシリアル受信は、シリアルの35μsタイミング待ちを30回呼び出して1文字受信ですから、受信した文字を毎回チェックする必要はありません。

おわりに

というわけで、ソースをざっと追ってみましたが、いかがでしたでしょうか。割り込みベースの処理に慣れている方だと、見なれないやり方で不思議に思われたかも知れません。クロック数を正確に読めるアセンブラならではのやり方として、心に留めていただければと思います。

最後までおつき合いいただき、ありがとうございました。

今回のプログラムソース全体はこちらからダウンロードできます。

おすすめ記事

記事・ニュース一覧