USBホストになれるCH559入門 その3 Lチカ振り返り

概要

LチカとUARTデバッグをしたところで、ドキュメントの翻訳をしていました。今回ある程度翻訳が進んできたので、Lチカのコードを再度ドキュメントを見ながら解析してきたいと思います。

Lチカソースコード

#include <stdint.h>
#include "ch559.h"

static inline void delay()
{
    uint32_t i;
    for (i = 0; i < (120000UL); i++)
    {
        __asm__("nop");
    }
}

void main()
{
    PORT_CFG = 0b00101101;
    P1_DIR = 0b11110000;
    P1 = 0x00;

    AIN4 = 0; // P1_4
    AIN5 = 1; // P1_5
    AIN6 = 0; // P1_6
    AIN7 = 1; // P1_7

    while (1)
    {
        delay();
        AIN4 = !AIN4;
        AIN5 = !AIN5;

        delay();
        AIN4 = !AIN4;
        AIN5 = !AIN5;
        AIN6 = !AIN6;
        AIN7 = !AIN7;
    }
}

上記がコードになります。

PORT_CFG設定

    PORT_CFG = 0b00101101;

上記を確認してみます。

SFR(PORT_CFG,	0xC6);	// port 0/1/2/3 config
#define bP3_DRV           0x80      // P3 driving capability: 0=5mA, 1=20mA
#define bP2_DRV           0x40      // P2 driving capability: 0=5mA, 1=20mA
#define bP1_DRV           0x20      // P1 driving capability: 0=5mA, 1=10mA
#define bP0_DRV           0x10      // P0 driving capability: 0=5mA, 1=20mA
#define bP3_OC            0x08      // P3 open-drain output enable: 0=push-pull output, 1=open-drain output
#define bP2_OC            0x04      // P2 open-drain output enable: 0=push-pull output, 1=open-drain output
#define bP1_OC            0x02      // P1 open-drain output enable: 0=push-pull output, 1=open-drain output
#define bP0_OC            0x01      // P0 open-drain output enable: 0=push-pull output, 1=open-drain output

定義を検索していると上記があります。0xC6ですね。定義の下にあるのが実際に設定する値だと思われます。

上記のドキュメントをあわせて見ていくことにします。

設定名ビット初期値設定した値備考
bP3_DRV0x8000P3ドライブ能力 0=5mA, 1=20mA
bP2_DRV0x4000P2ドライブ能力 0=5mA, 1=20mA
bP1_DRV0x2001P1ドライブ能力 0=5mA, 1=10mA
bP0_DRV0x1000P0ドライブ能力 0=5mA, 1=20mA
bP3_OC0x0811P3出力種別 0=プッシュプル, 1=オープンドレイン
bP2_OC0x0411P1出力種別 0=プッシュプル, 1=オープンドレイン
bP1_OC0x0210P0出力種別 0=プッシュプル, 1=オープンドレイン
bP0_OC0x0111P3出力種別 0=プッシュプル, 1=オープンドレイン

上記の設定になります。P1を出力に使うのでドライブ能力を5mAから10mAに引き上げています。Lチカだけだったら5mAでも大丈夫かもしれません。

あとは初期値がオープンドレインになっていますので、P1のみプッシュプルに指定しています。

    //PORT_CFG = 0b00101101;
    PORT_CFG = bP1_DRV | bP3_OC | bP2_OC | bP0_OC;

つまり、上記の書き方のほうがフレンドリですね。

    PORT_CFG |= bP1_DRV; // P1 10mA drive
    PORT_CFG &= ~bP1_OC; // P1 push-pull

個別に設定したほうが本当はわかりやすい気がします。ちなみにこの2つは設定しなくてもLチカできました。今回はP1_DIRの設定が重要でした。

P1_DIR設定

    P1_DIR = 0b11110000;

上記で入出力の方向を設定しているはずです。

SFR(P1_DIR,	0xBA);	// port 1 direction

上記の設定でした。こちらにはビット定義がないのかな。P1以外は定義がありましたがP1は汎用IOに主に使うのでないのかもしれません。

設定名ビット初期値設定した値備考
0x8001P1_7入出力 0:入力, 1:出力
0x4001P1_6入出力 0:入力, 1:出力
0x2001P1_5入出力 0:入力, 1:出力
0x1001P1_4入出力 0:入力, 1:出力
0x0800P1_3入出力 0:入力, 1:出力
0x0400P1_2入出力 0:入力, 1:出力
0x0200P1_1入出力 0:入力, 1:出力
0x0100P1_0入出力 0:入力, 1:出力

上記を設定していますね。P1にある8ピンから後半の4ピンを出力に設定しています。

    //P1_DIR = 0b11110000;
    //P1_DIR = ( 1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 );
    P1_DIR |= 1 << 7; // P1_7 Output
    P1_DIR |= 1 << 6; // P1_6 Output
    P1_DIR |= 1 << 5; // P1_5 Output
    P1_DIR |= 1 << 4; // P1_4 Output

こちらも直値よりはもう少しわかりやすい表記のほうが良さそうですね。本当はラッパー関数で設定したほうがいいのかもしれません。

SBITとSFR関数ってなんだ?

8051はビット単位でのアクセスが可能な命令があるみたいで、特殊な拡張のようです。

__sfr __atat (0x80) P0;  /* special function register P0 at location 0x80 */

上記のように宣言するとP0という名前で0x80のレジスタにアクセス可能です。

SFR(P0,	0x80);	// port 0 input & output

実際に定義は上記のようにしていますので、どこかで置換しているはずです。

#if defined (SDCC) || defined (__SDCC)
# define SBIT(name, addr, bit)  __sbit  __at(addr+bit)                    name
# define SFR(name, addr)        __sfr   __at(addr)                        name
# define SFRX(name, addr)       __xdata volatile unsigned char __at(addr) name
# define SFR16(name, addr)      __sfr16 __at(((addr+1U)<<8) | addr)       name
# define SFR16E(name, fulladdr) __sfr16 __at(fulladdr)                    name
# define SFR16LEX(name, addr)   __xdata volatile unsigned short __at(addr) name
# define SFR32(name, addr)      __sfr32 __at(((addr+3UL)<<24) | ((addr+2UL)<<16) | ((addr+1UL)<<8) | addr) name
# define SFR32E(name, fulladdr) __sfr32 __at(fulladdr)                    name

C:\Program Files\SDCC\include\mcs51\compiler.hで上記の定義がありました。ここで変換しているんですね。

   SBIT(P0_7,	0x80, 7);
   SBIT(P0_6,	0x80, 6);
   SBIT(P0_5,	0x80, 5);
   SBIT(P0_4,	0x80, 4);
   SBIT(P0_3,	0x80, 3);
   SBIT(P0_2,	0x80, 2);
   SBIT(P0_1,	0x80, 1);
   SBIT(P0_0,	0x80, 0);

SBITは上記のように定義しています。

__sbit __atat (0xd7) CY;  /* CY (Carry FlagFlagsCarry flag) */

実際には上記の形式に変換されているはずです。

まとめ

UART部分もやりたかったのですが、翻訳が終わっていないので次回にしたいと思います。

コメント

タイトルとURLをコピーしました