フォト
無料ブログはココログ

CPUボード

2018年4月24日 (火)

lcc1802

 以前書いた記事(CDP1802 (15/10/11))のコメントにCDP1802用のCコンパイラの質問があったので調べてみた。 

 結論は、出力されるバイナリが大きいので、RAMを十分載せておけば使える。

lcc1802

 CDP1802は入手したがまだ動かしていない。 (^^;
SBCを作ったとしても、おそらくtinyBASICでも動かしたら満足するのだろう。というわけで、CDP1802用のCコンパイラは調べていなかった。

 調べてみるとlcc1802が見つかった。
lccはOpen SourceのコンパイラでターゲットCPUはAlpha,SPARC,MIPS,x86に対応している。 lcc1802はlccをCDP1802に対応させたもので、COSMAC ELF Membersip Cardのアドオンシールドにarduinoを持たせた 1802 olduino 用に作られたもののようだ。 
https://sites.google.com/site/lcc1802/downloads
からダウンロードできる。

インストール

 lcc42judy.zipをインストールしてみた。
zipを解凍して現れるlcc42ディレクトリをCドライブ直下(C:\)にコピーしろと書いてある。
開発系は C:\Devzにインストールしているので、ここにインストールした。 コンパイル・ドライバ(lcc)が呼び出すコマンドは同じディレクトリを探すので問題はないだろうと考えたのだが、cppが見付からないと怒られた。(^^;
どうやら、プリプロセッサ(cpp.exe)、コンパイラ(rcc.exe)、アセンブラ(asw.exe)のパスは"C:\lcc42\bin"がハードコードしてあるようだ。 仕方がないので、C:\からinkを張っておいた。

エミュレータ

 まだSBCを作っていないのでCOSMACエミュレータ Emma 02で試してみた。
Emma 02はCDP1802が使われている数々のボードやシステムのエミュレーションができる。 その中でシンプルな VELFで試してみた。

Emma02_velf

Emma02_velf_frontpanel

 COSMAC ELFはシリアル・インタフェースを持っていない。Q(#4)をTXD、EF2(#23)をRXDに使ってソフトウェアで実装してある。

 シンプルな COSMAC ELFで試しておけばSBCを作るときにシリアルインタフェースを載せなくて良い。

サンプル

 lcc1802にはサンプルが用意されている。 これらのサンプルはolduino用なので、これを変更してVELFで動くようにする。

 まずはお約束の hello worldから。 examples/helloworldディレクトリにある。

↓helloworld.c

/*
   print the string "hello World!"
*/
#include <nstdlib.h>
void main()
{
printstr("hello World!\n");
}
#include <nstdlib.c>

ライブラリ

 4行目の #include <nstdlib.h>は良いが、最終行の #include <nstdlib.c>は何?である。

 lcc1802はライブラリはなくて、最低限の関数がinclude/nstdlib.cに書いてある。printf()もあるが、最低限の機能だけで出力幅指定はできない。

 文字列出力関数のprintstr()は1文字出力にout(5,d)を呼んでいるが、VELFでは動作しないのでputc()を使うように変更しておく。

↓nstdlib.c内のprintstr()

void printstr(char *ptr){
//    while(*ptr) out(5,*ptr++);
    while(*ptr) putc(*ptr++);
}

 nstdlib.cにはfloatを扱う関数がある。 sdccは賢いので必要な関数だけリンクしてくれるがlcc1802ではnstdlib.cはいつでもコンパイルされるので使わない関数でもオブジェクトに含まれる。float関係の部分だけは "nofloats"が宣言されているとコンパイルしないようになっている。 コマンドラインオプションで -Dnofloatsとすれば良い。

ランタイムライブラリ

 ランタイムライブラリは、include/lcc1802Prolo??.incとinclude/lcc1802Epiloxx.inc。

 これらはのライブラリは、コンパイル済のライブラリをlinkerがリンクするのではなく、ソースに展開している。ソース中にnsddlib.cをインクルードし、アセンブラソース中でlcc1802Prolo??.incとlcc1802Epiloxx.incをインクルードしている。

入出力ルーチン

 IOルーチンは、include/IO1802.incに書いてある。 printf()は1文字出力にputc()を呼んでいる。  lcc1802が想定しているolduinoの1文字出力はout(5,d)を使っているので、VELFでは出力できない。

 IO1802.incに1文字出力(putc())と1文字入力(getc())を書けば良さそうだ。
ここでハタと困った。CDP1802のアセンブラは書いたことが無い。
とりあえず、Emma02にVELFのモニタソース(Emma02\data\Velf\VELFbios-v3.1.LST)があったので、この中の1文字入出力ルーチンを参考に(ほぼコピペ)、Cから引数を渡す部分と戻り値を返す部分を付け加えて、テンポラリ変数をreからrt1に変えた。

↓IO1802.inc

;**********************************************************************
;Transmit Byte via Q connected to RS232 driver
;call with R3 being the previous PC
;Byte to send in D
;Returns with D unaffected
;re.1 = D
;Destroys rt1.0
;----------------------------------------------------------------------
_putc: glo regArg1
txchar: phi rt1
ldi 9 ;9 bits to transmit (1 start + 8 data)
plo rt1
ghi rt1
shl ;set start bit
rshr ;DF=0

txcloop:
bdf $+5 ;10.5   jump to seq to send a 1 bit
req ;11.5   send a 0 bit
br $+5 ;1      jump +5 to next shift
seq ;11.5   send a 1 bit
br $+2 ;1      jump +2 to next shift (NOP for timing)
rshr ;2      shift next bit to DF flag
phi rt1 ;3      save D in rt1.1
DEC rt1 ;4      dec bit count
glo rt1 ;5      get bit count
bz txcret ;6      if 0 then all 9 bits (start and data) sent
ghi rt1 ;7      restore D
NOP ;8.5    pause 1/2 time
br txcloop ;9.5    loop back to send next bit
txcret: ghi rt1 ;7
ghi rt1 ;8
ghi rt1 ;9
NOP ;10.5
seq ;11.5 stop bit
NOP ;1
NOP ;2.5
NOP ;4
NOP ;5.5
NOP ;7
NOP ;8.5
NOP ;9
NOP ;10.5
Cretn

;**********************************************************************
;rx_char
;Receive Byte via EF2 connected to RS232 rt1ceiver
;Recieves 8 bits
;call with R3 being the previous PC
;Returns with Byte received in D and rt1.1
;Destroys rt1.0
;----------------------------------------------------------------------
_getc:
rxchar: ldi 8 ;start bit +7 bits from loop, last bit on returning
plo rt1
ldi 0
rxcw: ;wait for start bit
bn2 rxcw ;each instr takes 9us, we need 104us = 11.5
;delay 1/2 bit time to center samples
NOP ;     Don't test for correct start bit
NOP ;     it will work, if there's too much
NOP ;     noise on the line, shorten the cable!
rxcloop:
NOP ;10.5
b2 $+6 ;11.5 sample rx input bit
ori 80h ;1
br $+4 ;2
phi rt1 ;1
phi rt1 ;2
shr ;3
phi rt1 ;4
DEC rt1 ;5
glo rt1 ;6
bz rxcret ;7
ghi rt1 ;8
br rxcloop ;9
rxcret: ghi rt1 ;8
ghi rt1 ;9
NOP ;10.5
b2 $+4 ;11.5 sample last rx input bit
ori 80h ;1
plo retVal
Cretn

 

コンパイル

 この変更でIntelHexフォーマットの a.hexが作られるようになる。

C:\Users\user> lcc -Dnofloats helloworld.c
And St. Judy's Compiler FLOATS Across your code...
P2HEX/C V1.42 Beta [Bld 87]
(C) 1992,2013 Alfred Arnold
C:\Users\user\AppData\Local\Temp/lcc124682.p==>>a.hex  (3609 Bytes)

しかし、hellowroldだけで3,609byteとは。 

8Queen

 8Queen問題を解くプログラムをコンパイルしてみる。 再帰を使っているのでちゃんと動くだろうか?

/*
* 8Queen.c
* Yoshi / Apr.2018
*/

#include <nstdlib.h>

#define FALSE 0
#define TRUE (!FALSE)
#define u_char unsigned char
#define u_short unsigned short
#define NL "\r\n"
#define getchar getc
int DEBUG = 0;

char table[8];
void clrtbl(void) { int i; for(i=0;i<8;i++) table[i]=0xFF; }

void printtb(char n) {
    int x, y;
    printf(NL "-- %d --" NL, n);
    for (y=0;y<8;y++) {
        putchar('|');
        for (x=0;x<8;x++) {
            if (table[y]==x)
                 putchar('Q');
            else putchar(' ');
            putchar('|');
        }
        puts(NL);
    }
}

char check(int x, int y) {
    int i;

    if (y==0) return TRUE;
    for (i=y-1; 0<=i; i--) {
        if (2<DEBUG) printf("tb[%d]=%d" NL, i, table[i]);
        if (table[i]==x || table[i]==(x-(y-i)) || table[i]==(x+(y-i)))
            return FALSE;
    }
    return TRUE;
}

char place(int x, int y) {
    static int n = 1;

    if (DEBUG) printf("place(%d,%d):" NL, x, y);

    if (7<y) {  /* 解 */
        printtb(n++);
        return FALSE;
    }
    if (7<x) {  /* 見つからない */
        return FALSE;
    }

    if (check(x,y)) {
        table[y] = x;
        if (n==1) printtb(n);
        place(0, y+1);
        table[y] = (u_char)-1;
    }
    return place(x+1, y);
}

void main(void) {
    printf("8 Queen" NL);
    clrtbl();
    place(0,0);
    exit(0);
}

#include <nstdlib.c>

コンパイルすると、4,542byteだった。

↓実行結果

Emma02_velf_terminal

ちゃんと実行されるようだ。

まとめ

 SBCを作るときはRAMをケチらないで載せておけばlcc1802は使える。今時、32kのRAMを積んでも大した出費ではない。

 残念ながら、オリジナルのCOSMAC ELFではRAMが少ないので使えないだろう。もっともオリジナルのCOSMAC ELFを忠実に作るなら、ハンドアセンブルして、8個のスイッチでプログラムを入力する覚悟が必要だ。

 rcc.exeがASxxxxアセンブラを吐くようにするとSDCCのライブラリが使えるようになる。そして、aslinkを使うともう出力がもう少しコンパクトになると思う。 興味はあるが、そこまでやるか... 



最近の投稿】【2017の投稿】【2016の投稿】【2015の投稿

最近のCOSMAC/CDP1802

2018年4月15日 (日)

IchigoJam(11) 

LED&KEY(TM1638)をIchigojamで点けてみた。BASICで入出力ポートを制御するとさすがに遅い。あまりに遅いので、マシン語で高速化してみた。

Tm1638_yoshi1
ダウンロード TM1638_Yoshi1.mp4 (1075.3K)

YOSHIがスクロールしているのだけれど、残像が残って認識できないくらい速くなっている。

 福野さんがこのボードをマシン語で制御しておられる。(2018/4/14)
(格安7セグディスプレイ「LED&KEY」を使ってカチカチカウンターづくり、TM1638 x IchigoJam用マシン語SPIドライバの作り方)

どの部分をマシン語で書くか

  このボードに載っているLED&KEY制御用デバイス(TM1638)はSPIもどきでデータを送り受けする。で8個のLEDを制御しようとすると、 8(LED)x16bit/LED+3(command)×8bit/cmd=152bit のデータを送る必要があるから、クロックも152個必要だ。
このクロックをBASICのOUT命令でON/OFFしているから、データI/Oが152回、クロックが304回で合計456回IN/OUT命令を実行しなければならない。

 高速化するならば、データを送り受けする部分をマシン語にすれば効果があるだろう。

 メモリダンプ・プログラムを書いたときの手法でやってみる。IchigoJam(4) (2015/06/01)
Cで書いてgccでコンパイルして、逆汗して、BASICのPOKE文を作る。
逆アセンブラはgccのツールチェーンに入っているobjdumpを使った。
逆汗リストからPOKE文の作成にはawkを使った。今どきならpythonオヤジ世代ならperlだがおじいちゃん世代はawkだ。
 (IJUtils (http://ijutilities.micutil.com/#Software)のIJBin2Pokeを使うと簡単にできるらしい)

Cソース

 Cのソースはこんな感じ。

/*
* access.c
* Apr.2018/Yoshi
*/
#include <stdio.h>
#include <stdlib.h>

#define    GPIO1_MASKED_ACCESS    ((volatile uint32_t *)0x50010000)
#define    GPIO0_MASKED_ACCESS    ((volatile uint32_t *)0x50000000)

#define    BIT_LED    5    //GPIO1_5
#define    BIT_STB    0    //GPIO1_0
#define    BIT_CLK    1    //GPIO1_1
#define    BIT_DIO    5    //GPIO0_5

#define    STB_L    0
#define    STB_H    (1<<BIT_STB)
#define    CLK_L    0
#define    CLK_H    (1<<BIT_CLK)

#define    STB(d)    GPIO1_MASKED_ACCESS[1<<BIT_STB]=(d)
#define    CLK(d)    GPIO1_MASKED_ACCESS[1<<BIT_CLK]=(d)
#define    CLKIN()   GPIO1_MASKED_ACCESS[1<<BIT_CLK]
#define    DI()      GPIO0_MASKED_ACCESS[1<<BIT_DIO]
#define    DO(d)     GPIO0_MASKED_ACCESS[1<<BIT_DIO]=(d)

#define    GPIO0_DIR    *((volatile uint32_t *)0x50008000)
#define    GPIO0_10_IN    0
#define    GPIO0_10_OUT    (1<<BIT_DIO)

#pragma GCC optimize("O1")
void wait(void) {
    int w=5;
    while (0<w) {
        w--;
        CLKIN();
    }
}
#pragma GCC optimize("Os")

/*
* receive 16bit
* uint32_t recv(uint32_t data)
* arg : uint32_t data : receive data
* ret : uint32_t : receive data
*/
uint32_t recv(uint32_t data) {
    int i;
    GPIO0_DIR &= ~(1<<BIT_DIO);    //DIO -> input
    for (i=0; i<16; i++) {
        CLK(CLK_L);
        wait();
        data <<= 1;
        data |= DI();
        CLK(CLK_H);
        wait();
    }
    data >>= BIT_DIO;
    return data;
}

/*
* send 8/16bit data
* uint32_t send(uint32_t data)
* arg : uint32_t send data
* ret : uint32_t :
* rem : send data : 1xxx xxxx CCCC CCCC : command (8bit)
*                   0xxx xxDD DDDD DDDD : data(16bit)
*/
uint32_t send(uint32_t data) {
    int i = (data & 0x8000)? 8: 0;    //check command flag
    data <<= BIT_DIO;
    GPIO0_DIR |= (1<<BIT_DIO);        //DIO -> output
    STB(STB_L);
    for (; i<16; i++) {
        CLK(CLK_L);
        DO(data);
        data >>= 1;
        wait();
        CLK(CLK_H);
        wait();
    }
}

void main(void) {
    wait();
    recv(0);
    send(0);
}

  TM1638にデータを送る関数はuint32_t send(uint32_t data)でデータを受ける関数はuint32_t recv(uint32_t dummy)だ。

 IchigojamのUSR関数の解説では、hint16_t foo(uint16_t arg , uint8_t* mem, uint8_t* font)となっているが気にしない。

 第1引数にはUSR(address , num)で呼んだときのnumがR0レジスタ経由で渡される、第2引数には仮想メモリの先頭アドレスが、第3引数にはキャラクタ・パターン・テーブルの先頭アドレスが渡されるが今回は使わないので宣言しない。

 ARMの関数呼び出し規約では、4個までの引数はR0~R3レジスタを使い、5個目以上の引数はスタック経由で渡すらしい。 第2引数(uint8_t* mem)、第3引数(uint8_t* font)を宣言すると、R1とR2レジスタをコンパイラが使わなくなるので第2、第3引数は宣言しなかった。

マスクアクセス(masked access)

 Ichihojamに使われているCPU LPC1114FN28には、マスク・アクセスという機能がある。福野さんのサイトに解説がある。
(マシン語でLEDを光らせよう! - IchigoJamではじめるArmマシン語その4)

 一般に出力ポートの特定のビットを制御するときには、他のビットに影響しないようにする必要がある。
C言語では、出力ポートPxに'1'や'0'を出力するには、Px |= (1<<BIT)、Px &= ~(1<<BIT)
と書くことが多い。

もっと詳しく書くと
 W <- Px
 W <- W & (1<<BIT)
 W <-  DATA << BIT
 Px <- W

という処理が必要だ。つまり、変数Wに一旦ポートのデータを読んできて書き換えたいビットだけ書き換えてもう一度ポートに出力する処理が必要になる。

 簡単に言うと、この面倒な処理を一度のアクセスで実現するのがマスク・アクセス機能で、GPIOのベース・アドレス付近にこの機能が割り当てられている。

 C言語的には、ベースアドレスに配列 uint32_t MSKED_ACCESS[4096] があって。配列の要素がそれぞれビット・パターン(マスクパターン)に対応している。

 例えば、IchigojamのGPIO1のbit(5)に接続されているLEDを制御する場合。

 GPIO1のベースアドレスは 0x50010000で、データ・レジスタ GPIO1_DATA は 0x500103FFCにマッピングされている。

 LEDを点灯させるには GPIO1_DATA(5)に'1'を出力すればよいから

 GPIO1_DATA |= (1<<5);

となる。これをマスク・アクセスを使うと

 GPIO1_MASKED_ACCESS[1<<5] = (1<<5);

でよい。出力する値は I<<5= 0b00010000でなくても、5ビットが1であれば良く、0b11111111でも良い。

 Cで書くと手間はあまり変わらないが、マシン語に翻訳すると

      GPIO1_DATA |= (1<<5);

    movs    r3, #32     ; r3 <- 1<<BIT_LED
    ldr     r2, .L2     ; r2 <- &PIO1_DATA
    ldr     r1, [r2]    ; r1 <- *PIO1_DATA
    orrs    r3 r1       ; r1 <- *PIO1_DATA | (1<<BIT_LED)
    str     r3, [r2]    ;(PIO1_DATA <- r1
    bx      lr          ; return_
    .align  2
.L2:
    .word    0x50013FFC

   GPIO1_MASKED_ACCESS[1<<5] = (1<<5);

    movs    r2, #32     ; r2 <- 1<<BIT_LED
    ldr     r3, .L5     ; r3 <- &PIO1_MASKED_ACCESS[1<<BIT_LED]
    str     r2, [r3]    ; PIO1_MASKED_ACCESS[1<<BIT_LED] <- 1<<BIT_LED
    bx      lr          ; return    ;
    .align   2
.L5:
    .word    0x50010008 ; &PIO1_MASCED_ACCESS[1<<BIT_LED]

コードは短くなっている。

 コードが減るより重要なことは、ポートアクセスは一旦ポートの状態を読んでいるので、処理中にポートの状態が変わってしまうと誤動作してしまう。 ところが、マスクアクセスはポートの状態を読み出さないので、誤動作する心配がないということである。

ウエイト(時間待ち)

 Cのソースはごく普通に16/8bitのデータを受けて1ビットづつ出力する処理だ。

 TM1638のデータシートを見るとクロックの最小パルス幅(PWCLK)は400ns、データセットアップタイム(tSETUP) データホールドタイム(tHOLD)はそれぞれ100nsとなっている。

 BASICは遅いからクロック出力はOUT 2,0/OUT 2,1で良いが、
マシン語に置き換えると速すぎる。'1'又は'0'を出力して400nsは出力データが変わらないように時間待ちする必要がある。

最適化

 gccは-Oオプションで最適化レベルを指定できる。実行速度よりコード領域を最小化するオプション -Os は強力だ。 Ichigojamのようにマシン語で使える領域が少ない場合には有難い。

 とこrが、-Osオプションで最適化すると、ループを回って時間待ちするルーチンは最適化されて無くなってしまう。

 void wait(void { int i; for (i=0;i<100;i++) ; }

の結果はどこにも影響を与えないので無くても良いということだけど、ちょっと困る。

 最適化されないようにするには、wati()関数内で volataile属性の変数にアクセスすれば良い。 volatile属性は、例えばIO関係のレジスタや割り込みで使用される変数のようにアクセスするたびに内容が変わるという意味だ。

 つまり、コンパイラに「あんたが知らないうちに値が変わってる変数だヨ」と教えておくと、最適化しないでくれる。

 これをコンパイルして、wati(),recv(),send()をBASICのPOKE文にすると

REM wait()
POKE #700,#03,#4B,#1A,#68,#1A,#68,#1A,#68,#1A,#68,#1B,#68,#70,#47,#C0,#46,#08,#00,#01,#50
REM recv()
POKE #714,#20,#21,#F8,#B5,#04,#00,#10,#25,#02,#27,#0B,#4A,#13,#68,#8B,#43,#13,#60
POKE #726,#00,#23,#09,#4E,#33,#60,#FF,#F7,#E8,#FF,#08,#4B,#1B,#68,#64,#00,#37,#60
POKE #738,#01,#3D,#1C,#43,#FF,#F7,#E0,#FF,#00,#2D,#F0,#D1,#60,#09,#F8,#BC,#02,#BC
POKE #74A,#08,#47,#00,#80,#00,#50,#08,#00,#01,#50,#80,#00,#00,#50
REM send()
POKE #758,#20,#21,#0F,#4A,#F8,#B5,#13,#68,#0B,#43,#13,#60,#00,#22,#02,#27,#0C,#4B
POKE #76A,#04,#04,#E4,#0F,#1A,#60,#E4,#00,#45,#01,#00,#23,#0A,#4E,#33,#60,#0A,#4B
POKE #77C,#1D,#60,#FF,#F7,#BF,#FF,#01,#34,#37,#60,#6D,#08,#FF,#F7,#BA,#FF,#10,#2C
POKE #78E,#F1,#D1,#F8,#BC,#02,#BC,#08,#47,#C0,#46,#00,#80,#00,#50,#04,#00,#01,#50
POKE #7A0,#08,#00,#01,#50,#80,#00,#00,#50

BASICソース

BASICのプログラムはこんな感じ

1 REM TM1638 demo2
2 CLV: VIDEO 0
3 REM === ===
3 REM [0]-[7]    :7SEG data:
3 REM [8]        :guard
3 REM [9]        :LED data
3 REM [20]-[22]  :key
3 REM [23]-[26]  :key scan data
3 REM
3 REM === FONT ===
3 REM  -- a       dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM |f |b     0: 0011 1111 1: 0000 0110 2: 0101 1011 3: 0100 1111
3 REM  -- g       dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM |e |c     4: 0110 0110 5: 0110 1101 6: 0111 1101 7: 0000 0111
3 REM  -- d .dp   dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM           8: 0111 1111 9: 0110 1111 A: 0111 0111 B: 0111 1100
3 REM             dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM           C: 0011 1001 D: 0101 1110 E: 0111 1001 F: 0111 0001
3 LET [80],#3F,#06,#5B,#4F, #66,#6D,#7D,#07, #7F,#6F,#77,#7C, #39,#5E,#79,#71
4 REM             dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
4 REM           H: 0111 0110 L: 0011 1000 G: 0011 1101 Y: 0110 1110
4 REM             dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
4 LET [96],#76,#38,#3D,#6E
5 D=3        :'Duty:1/16
6 'P=0        :'Position 0
7 'L=8        :'Length 8

100 REM === MAIN ===
120 REM I/O STB:1 CLK:2 DI:IN3/OUT10
120 OUT 1,1: OUT 2,1
130 GOSUB @DSPOFF
135 @SCRL
140 REM SCROOL
140 LET [0],0,0,0,[99],[80],[85],[96],[81],0    :'"YOSHI"
150 @LOOP1
160 GOSUB @GETKEY: [9]=[20]
170 'PRINT HEX$([23],4);" ";HEX$([24],4);" ";BIN$([20],8)
170 PRINT BIN$([20],8);:FOR I=0 TO 7:?CHR$(8);:NEXT
180 N=N+1:
190 IF [20]=#81 THEN GOSUB @DSPNUM: GOTO @SCRL
200 'ELSE
210  P=0: L=8: GOSUB @DSP7SEG
220  [8]=[0]: COPY #800, #802, 16
280 GOTO @LOOP1

500 REM Disp number
500 REM N:number
500 REM W:work Y:counter
500 @DSPNUM
510 W=N: LET[0],0,0,0
520 FOR Y=7 TO 3 STEP -1
530   IF W=0 THEN [Y]=0
540   [Y]=[80+W%10]: W=W/10
550 NEXT
560 GOSUB @DSP7SEG
570 RETURN

600 REM    KeyScan
600 REM    Command=#42:Rread key-scan data
600 REM    [20]:K1 [21]:K2 [22]:K3 [23]-[26] scan data
600 REM S:SendData R:RecvData Y:counter
600 @GETKEY
610 S=#42: GOSUB @SENDC
620 FOR Y=23 TO 24
630   GOSUB @RECV
640   [Y] = R
650 NEXT
660 OUT 1,1    :'STB off
670 X=#8888
680 FOR Y=0 TO 2: U=Y+20
690   [U]=    ([23]&X)>>(1-Y)        :'-1---5---2---6--
690   [U]=[U]|([24]&X)>>(3-Y)        :'-1-3-5-7-2-4-6-8
700   [U]=([U]&#FF)|([U]&#FF00)>>7   :'--------12345678
710   X=X>>1
720 NEXT
730 RETURN
                   
800 REM DISP OFF
800 REM S:Send data #80=Command:Display Off
800 @DSPOFF
810 S=#80+D: GOSUB @SENDC: OUT 1,1
820 RETURN

900 REM DISP 7SEG (address add mode)
900 REM D: Dimmer 0-7 #80:Disp OFF
900 REM P: Position
900 REM L: Length
900 REM [0]-[7]: 7SEGx8, [9]:LEDx8
900 @DSP7SEG
910 S=#40:GOSUB @SENDC: OUT 1,1        :'#40: write register | auto increment
920 S=#C0+P*2: GOSUB @SENDC            :'address
930 FOR I=0 TO (L-1)
940   S=[P+I]: GOSUB @SENDD            :'7SEG
960 NEXT
970 OUT 1,1                            :'STB off
980 @DSPON: S=#88 | D: GOSUB @SENDC: OUT 1,1    :'88H:disp on
990 RETURN

1000 REM R:Recv data(16bit)
1000 @RECV
1000 R=USR(#714,0)
1010 'OUT 10,-1            :'OUT10->IN3
1020 'FOR X=1 TO 16
1030 '  OUT 2,0            :'OUT2:CLK
1040 '  R = R<<1: R=R+IN(3)    :'IN3, DI
1050 '  OUT 2,1            :'OUT2:CLK
1060 'NEXT
1070 RETURN

1100 REM S: Send data
1100 REM 1xxxxxxx CCCCCCCC : command(8bit)
1100 REM 0xxxxxDD DDDDDDDD : data(10bit)
1100 @SENDC        :'send command
1110 S=S|#8000
1120 @SENDD        :'send data
1130 X=USR(#758,S)
1130 'OUT 1,0        :'STB on
1140 'M=16: IF S&#8000 THEN M=8
1150 'FOR X=1 TO M
1160 '  OUT 2,0        :'OUT2:CLK
1170 '  OUT 10, S&1    :'OUT3:DIO
1180 '  OUT 2,1        :'OUT2:CLK
1190 '  S = S>>1
1200 'NEXT
1210 RETURN

コマンドは8ビット、データは16ビットで送受信するように変えた。

 メモリ容量が足りないので、ソースの中にマシン語が書けない。
マシン語をメモリに書き込む処理は最初に実行すれば良い。そしてプログラム実行中に書き換える必要性はないので、プログラム実行前にダイレクトモードで実行すればよい。メモリにセーブしたい場合には、別プログラムにしてLRUNで呼び出すことになる。

 1024バイトに収まるようにパックすると、

10 'TM1638 demo2
20 CLV:VIDEO 0
30 LET[80],#3F,#06,#5B,#4F,#66,#6D,#7D,#07,#7F,#6F,#77,#7C,#39,#5E,#79,#71
40 LET[96],#76,#38,#3D,#6E:D=3:OUT 1,1:OUT 2,1:GSB @DSPOFF
50 @SCRL:LET[0],0,0,0,[99],[80],[85],[96],[81],0
60 @LOOP1:GSB @GETKEY:[9]=[20]:? BIN$([20],8):FOR I=0 TO 7:?CHR$(8);:NEXT:N=N+1
70 IF[20]=#81 GSB @DSPNUM:GOTO @SCRL
80 P=0:L=8:GSB @DSP7SEG:[8]=[0]:COPY #800,#802,16:GOTO @LOOP1
90 @DSPNUM:W=N:LET[0],0,0,0:FOR Y=7 TO 3 STEP-1:IF W=0 [Y]=0
100 [Y]=[80+W%10]:W=W/10:NEXT:GSB @DSP7SEG:RTN
110 @GETKEY:S=#42:GSB @SENDC:FOR Y=23 TO 24:GSB @RECV:[Y]=R:NEXT:OUT 1,1:X=#8888
120 FOR Y=0 TO 2:U=Y+20:[U]=([23]&X)>>(1-Y):[U]=[U]|([24]&X)>>(3-Y)
130 [U]=([U]&#FF)|([U]&#FF00)>>7:X=X>>1:NEXT:RTN
140 @DSPOFF:S=#80+D:GSB @SENDC:OUT 1,1:RTN
150 @DSP7SEG:S=#40:GSB @SENDC:OUT 1,1:S=#C0+P*2:GSB @SENDC:FOR I=0 TO(L-1):S=[P+I]
160 GSB @SENDD:NEXT:OUT 1,1
170 @DSPON:S=#88|D:GSB @SENDC:OUT 1,1:RTN
180 @RECV:R=USR(#714,0):RTN
190 @SENDC:S=S|#8000
200 @SENDD:X=USR(#758,S):RTN
'974bytes

マシン語

 わざわざ窮屈なメモリ環境でBASICからマシン語を使わなくて良いと思う。 Cで書いていると「全部Cで書いたらいいじゃないか」と思ったりして...

 趣味やパズルだと考えるとやる気がわいてくるのだけれど。^^)



最近の投稿】【最近のCPUボード】 【最近のIchigoJam】 【2017の投稿】 【2016の投稿】 【2015の投稿

2018年4月 8日 (日)

IchigoJam(10)

 何気なくaitendoを見ていたら、マイコンボードにちょうど良さげな表示器を見つけた。

aitendoで売ってるボードはたいていamazonでも売っているので調べたら、270円で売っていたのでAmazonで買った。今は2個で850円に値上がりしている。

Ledkey

 TM1638はTitan Micro ElectronicsのLED表示用ICで、英語版のデータシートは(https://www.mikrocontroller.net/attachment/332035/TM1638_V1.3_EN.pdf)にある。

 このICは7セグLED10桁の表示と、8x3のキー・マトリクスのスキャンができる。8279に似てると言ってわかるのはオジサンだろう。

 インタフェースはSPIモドキ。 TM1638はSIとSOが分離していない(オープンドレイン)ので、SPIで接続するならMaster側のMOSIとMISOを接続して使う。出力が衝突する可能性があるのでMOSIに1kΩの抵抗を入れておく。

Connect_spi

 サンプルはたくさんある。特にarduino用のサンプルは検索するとたくさん見つかる。

Ichgojamでやってみた。 IchigojamはI2C用にオープンドレイン出力のOUT10(IN3)がある。これをDI/DOUTに使うと簡単だ。 STBとCLKはOUT1,OUT2でON/OFFする。

↓こんな感じ

1 REM TM1638 
2 CLV: VIDEO 0
3 REM === ===
3 REM [0]-[7]	:7SEG data: 
3 REM [8]	:LED data
3 REM [20]-[22]	:key
3 REM [23]-[26]	:key scan data
3 REM
3 REM === FONT ===
3 REM  -- a       dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba 
3 REM |f |b     0: 0011 1111 1: 0000 0110 2: 0101 1011 3: 0100 1111 
3 REM  -- g       dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM |e |c     4: 0110 0110 5: 0110 1101 6: 0111 1101 7: 0000 0111
3 REM  -- d .dp   dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM           8: 0111 1111 9: 0110 1111 A: 0111 0111 B: 0111 1100
3 REM             dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
3 REM           C: 0011 1001 D: 0101 1110 E: 0111 1001 F: 0111 0001
3 LET [80],#3F,#06,#5B,#4F, #66,#6D,#7D,#07, #7F,#6F,#77,#7C, #C9,#5E,#79,#71
4 REM             dpgfe dcba   dpgfe dcba   dpgfe dcba   dpgfe dcba
4 REM           H: 0111 0110 L: 0011 1000 G: 0011 1101 -: 0100 0000
4 LET [96],#76,#38,#3D,#40
5 D=3		:'Duty:1/16
6 'P=0		:'Position 0
7 'L=8		:'Length 8

100 REM === MAIN ===
120 REM I/O STB:1 CLK:2 DI:IN3/OUT10
120 OUT 1,1: OUT 2,1 
130 GOSUB @DSPOFF
135 @SCRL
140 REM SCROOL
140 LET [0],0,0,0,[96],[94],[97],[97],[80]	:'"HELLO"
150 @LOOP1
160 GOSUB @GETKEY: [8]=[20]
170 'PRINT BIN$([20],8);:FOR I=0 TO 7:?CHR$(8);:NEXT
180 N=N+1: 
190 IF [20]=#81 THEN GOSUB @DSPNUM: GOTO @SCRL
200 'ELSE
210  P=0: L=8: GOSUB @DSP7SEG
220  W=[0]
230  FOR I=0 TO 6
240    [I]=[I+1]
250  NEXT
260  [7]=W
270 GOTO @LOOP1

500 REM Disp number
500 REM N:number
500 REM W:work Y:counter
500 @DSPNUM
510 W=N: LET[0],0,0,0
520 FOR Y=7 TO 3 STEP -1
530   IF W=0 THEN [Y]=0
540   [Y]=[80+W%10]: W=W/10
550 NEXT
560 P=3: L=5: GOSUB @DSP7SEG
570 RETURN

600 REM	KeyScan
600 REM	Command=#42:Rread key-scan data
600 REM	[20]:K1 [21]:K2 [22]:K3 [23]-[26] scan data
600 REM S:SendData R:RecvData Y:counter
600 @GETKEY
610 S=#42: GOSUB @SENDC
620 FOR Y=23 TO 26
630   GOSUB @RECV
640   [Y] = R
650 NEXT
660 OUT 1,1	:'STB off
670 LET [20],0,0,0:
680 FOR Y=0 TO 3
690   [20]=[20]|([Y+23])>>Y
700 NEXT
720 RETURN

800 REM DISP OFF
800 REM S:Send data #80=Command:Display Off
800 @DSPOFF
810 S=#80+D: GOSUB @SENDC: OUT 1,1
820 RETURN

900 REM DISP 7SEG (address add mode)
900 REM D: Dimmer 0-7 #80:Disp OFF 
900 REM P: Position
900 REM L: Length
900 REM [0]-[7]: 7SEGx8, [8]:LEDx8
900 @DSP7SEG
910 S=#40:GOSUB @SENDC :OUT 1,1	:'#40: write register | auto increment
920 S=#C0+P*2: GOSUB @SENDC 	:'address
930 FOR I=0 TO (L-1)
940   S=[P+I]: GOSUB @SENDD	:'7SEG
950   S=([8]>>I)&1: GOSUB @SENDD:'LED
960 NEXT
970 OUT 1,1			:'STB off
980 @DSPON: S=#88 | D: GOSUB @SENDC: OUT 1,1	:'88H:disp on
990 RETURN

1000 REM R:Recv data
1000 @RECV
1010 OUT 10,-1			:'OUT10->IN3
1020 FOR X=1 TO 8
1030   OUT 2,0			:'OUT2:CLK
1040   R = R<<1: R=R+IN(3)	:'IN3, DI
1050   OUT 2,1			:'OUT2:CLK
1060 NEXT
1070 RETURN

1100 REM S: Send data
1100 REM need STB ON
1100 @SENDC		:'send command
1110 OUT 1,0		:'STB on
1120 @SENDD		:'send data
1130 FOR X=1 TO 8
1140   OUT 2,0		:'OUT2:CLK
1150   OUT 10, S&1	:'OUT3:DIO
1160   OUT 2,1		:'OUT2:CLK
1170   S = S>>1
1180 NEXT
1190 RETURN

↑間違っていたので修正(2018/4/11) >(690   [20]=[20]|([Y+23]X)>>Y)

↑これでは1024バイトに収まらないので、省略形の命令に変えて、スペースを取って、マルチステートメントに変える。↓

10 ' TM1638
20 CLV:VIDEO 0
30 LET[80],#3F,#06,#5B,#4F,#66,#6D,#7D,#07,#7F,#6F,#77,#7C,#C9,#5E,#79,#71
40 LET[96],#76,#38,#3D,#40:D=3:OUT 1,1:OUT 2,1:GSB @DSPOFF
50 @SCRL:LET[0],0,0,0,[96],[94],[97],[97],[80]
60 @LOOP1:GSB @GETKEY:[8]=[20]:N=N+1:IF[20]=#81 GSB @DSPNUM:GOTO @SCRL
70 P=0:L=8:GSB @DSP7SEG:W=[0]:FOR I=0 TO 6:[I]=[I+1]:NEXT:[7]=W:GOTO @LOOP1
80 @DSPNUM:W=N:LET[0],0,0,0:FOR Y=7 TO 3 STEP-1:IF W=0[Y]=0
90 [Y]=[80+W%10]:W=W/10:NEXT:P=3:L=5:GSB @DSP7SEG:RTN
100 @GETKEY:S=#42:GSB @SENDC:FOR Y=23 TO 26:GSB @RECV:[Y]=R:NEXT:OUT 1,1
110 LET[20],0,0,0:FOR Y=0 TO 3:[20]=[20]|([Y+23]X)>>Y:NEXT:RTN
120 @DSPOFF:S=#80+D:GSB @SENDC:OUT 1,1:RTN
130 @DSP7SEG:S=#40:GSB @SENDC:OUT 1,1:S=#C0+P*2:GSB @SENDC:FOR I=0 TO(L-1):S=[P+I]
140 GSB @SENDD:S=([8]>>I)&1:GSB @SENDD:NEXT:OUT 1,1
150 @DSPON:S=#88|D:GSB @SENDC:OUT 1,1:RTN
160 @RECV:OUT 10,-1:FOR X=1 TO 8:OUT 2,0:R=R<<1:R=R+IN(3):OUT 2,1:NEXT:RTN
170 @SENDC:OUT 1,0
180 @SENDD:FOR X=1 TO 8:OUT 2,0:OUT 10,S&1:OUT 2,1:S=S>>1:NEXT:RTN

↑間違っていたので修正(2018/4/11) (110 LET[20],0,0,0:FOR Y=0 TO 3:[20]=[20]|([Y+23]X)>>Y:NEXT:RTN)

↓動かしたところ

Tm1638

ダウンロード TM1638.mp4 (1058.8K)

 さすがに遅い。 ソフトでCLKをON/OFFしているのが原因だ。

 もう少し早くなりそうだが。


2018年4月 6日 (金)

若松通商で買い物

若松通商SBC6800SBC6809に使うSRAM 6246ALSPを買った。 (若松はクレジットカードが使えるようになったようだ)

Hm6264alsp

300mil幅の64k SRAM。300mil幅の256kと16kは持っているんだけど64kは持ってなかった。

ついでに買った物は

IntelのEE80C188とAMDのN80C188

Ee80c188 80c188

IntelのCPUのプリフィクスってEEだったんだと知った。今更。

Intelの16bitCPUは8086で外部バスを8bitにしたのが8088。 16bitCPU8086にIOを内蔵して組み込み用にしたのが80186。

外部バスが16bitだと一般的なEPROM(27Cxxx)が2個、RAM(62xxx)が2個必要となる。80188は外部バスが8bitだからEPROMとRAMは1個づつでよい。

何かに使う目的があるわけではなく趣味で動かしてみることが目的だ。一品物なので手配線で作っているとEPROMとRAMが減ると配線が減るのでありがたい。

8088や80186は持っているのだが配線が面倒だから80188を探していた。2個も必要ないんだけど...

MC68HC11

Mc68hc11f1fn

68HC11にはそこまで思い入れはないのだけれど、HD63B03の開発環境を調べていて、68HC11の情報もたくさん見たので1つくらい持っておくかと。

↑のサフィックスは1Fだけど8AにはBUFFALOというブートローダーが入っている。若松にはA8はないみたいだ。できればDIPが欲しいのだけど。

アクティブ・ディレーライン

Adl100s

初めて見たような気がする。

ついでにZ80

Z84c0006

ZilogのZ80をCMOS化した東芝のTMPZ84C00があって、これを本家Zilogが作っているのがZ84C00。

最初に買ったZ80はシャープのLH0080だった。PC-8001にはuPD780が入っていたけど。


最近の投稿】 【最近の6809】 【最近のCPUボード】 【2017の投稿】 【2016の投稿】 【2015の投稿

2018年4月 2日 (月)

SBC6800、SBC6809

 モトローラ6800伝説で紹介されているSBC6800、SBC6809のプリント基板スイッチサイエンスで売られているので、1枚づつ買ってみた。

Rusekit

上がSBC6800用、下がSBC6809用。

 DCジャックは買い置きがあるのだけれど、このDCジャックは丸穴に刺さるタイプ。このタイプのDCジャックはプラグを抜き差しするときに力がかかるので強度的にどうだろう?

dcjack

 aitendoでSBC6800用のプリント基板とICを除いたパーツセットを売っているようだ。目ざといなaitendo。

 MC6800はクロックを作るのが大変でMC6871(クロックジェネレータ)の入手は難しい。
MC6871とかMC6875があるようだけど見たことが無い。でもこんなのを持っていたりする。

Mb8867

 MB8867はMB8861(富士通の6800)用のクロックジェネレータ―だ。(どこで見付けたんだったか?)

 MC6871やMC6875は簡単には入手できないので、8ピンPIC(PIC12F1288)で、クロックを作るのは現実的だ。

 MC6850(ACIA)もボーレートジェネレータが内蔵されていないので、通信速度の16倍のクロックが必要になる。

 こだわるなら、MC6840(PTM)とかMC14411を使うのだろうが、MPU用のクロックを8ピンPICで作ったのなら、ボーレートジェネレータも8ピンPICで作るのもありだろう。作りやすさを考えてのことかな。

Pic12f1822

 昔のマイコンで遊んでいると、どこまで昔の部品を使おうかと悩むことが多い。

###
 AVR派なのでPICを持ってない。PIC12F1288買わなくては。 ATTiny45でもできそうだけど。


最近の投稿】 【最近の6809】 【最近のCPUボード】 【2017の投稿】 【2016の投稿】 【2015の投稿






2018年3月27日 (火)

モトローラ6800伝説

モトローラ6800伝説 鈴木哲哉 ラトルズ

前から気になっていたので丸善に立寄ったついでに買ってきた。

6800

帯を見ると会員登録するとpdf版がダウンロードできるそうだ。  でもCD-ROM付きで売っていた。
68001 6800cd

 MC6800はクロックを作るのが面倒だから、クロックジェネレータを内蔵しているMC6802の方が使いやすい。

 この本では、8pinPICで面倒なクロックを作っている。 こだわる人はMC6871等を使うのだろう。

 この本で紹介されているプリント基板はスイッチサイエンスで売られている。

MikbugやVTLが書かれたUV-EPROMも売っているので簡単に楽しむことができそうだ。

###

 マイコンの勉強を始めたのは松本吉彦氏の「私だけのマイコン設計&製作」だった。


(↑ https://twitter.com/dream_library_/status/781940463410421761

この本を買ったのは1978年頃。

 当時田舎ではMPUは売っていなかったからトラ技の広告をチェックしていた。MC6800、MC6810、MC6820も高かったけど、MC6871(クロックジェネレータ)が高かった記憶がある。この本のとおりに作ろうとすると、到底小遣いで買える値段ではなかった。

 昔買えなかった物は40年経った今でも欲しくなるんだよね。


最近の投稿】【最近の書籍・雑誌】 【最近の6809】 【最近のCPUボード】【2017の投稿】【2016の投稿】【2015の投稿

2017年11月 4日 (土)

秋葉で買い物 <千石も模様替え>

久々に秋月が開いている時間に秋葉に行ったので前から欲しかったものを買ってきた。

Lcmxo22561 Lcmxo2256
↑LCMXO2-256
LatticeのMachXO2の一番小さいやつ。Mico8が載りそうな気がする。

Tmp92mc22fg1 Tmp92mc22fg
↑TMP92CM22FGと変換基板

TMP92CM22FGは東芝のTLCS-900/H1シリーズのマイコン。

TLCS-900/H1売ってる!と思って思わず買ってしまった。QFP100の変換基板も買ってきたけど、最近はんだ付けしていないから腕がなまっているだろうな。

TLCS-900/H1はZ80ライクのTLCS-90を16ビット化したTLCS-900を32ビット化したもの。
キングジムのポメラに使われているらしい。(↑じゃないけど。)
面白そうなので、ヤフオクでポメラの中古を探したのだけれど高くて断念した。

Irmodule
↑赤外線受信モジュール。安くなったよね。

 ついでに千石にも寄ったら旧本店がなくなって隣り2店舗に変わっていた。
どこに何を売っているか分からなくなったので一通り回ってみた。
本店地下は旧本店のB1,2Fが合体したような感じ。

↓こんなのを売っていたので買ってきた。

Coil Coil1
↑角型コイル 400μHと800μHの2種類があったので、800μHの方を買ってきた。


最近の投稿】【最近のCPUボード】【2016の投稿】【2015の投稿

2017年10月 8日 (日)

STC12C2052(2) <8Queen問題>

aitendoで買ったSTCmicroのSTC12C2052でプログラミングできるようになったので、8Queen問題を解くプログラムを走らせてみた。

 以前作った8031SBC用に書いたソースを変更した。 (8Queen2011/09/15)
 8031(8051のROM無し)は内臓RAMが128byteしかないので、スタックを消費する再帰プログラムは厳しい。
 ↓オプション --idata_loc 0x0000 --idata_size 128(8031用)でコンパイルするとスタックは98byteとれた。

-- 8031; ---
Internal RAM layout:
      0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|a|b|b|c|d|e|e| |
0x10:| | | | | | | | | | | | | | | | |
0x20:|T|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:| | | | | | | | | | | | | | | | |
0x90:| | | | | | | | | | | | | | | | |
0xa0:| | | | | | | | | | | | | | | | |
0xb0:| | | | | | | | | | | | | | | | |
0xc0:| | | | | | | | | | | | | | | | |
0xd0:| | | | | | | | | | | | | | | | |
0xe0:| | | | | | | | | | | | | | | | |
0xf0:| | | | | | | | | | | | | | | | |
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute

STC12C2052は256byteの内臓RAMが載っているので、オプション --idata_size 240でコンパイルするとスタックは192byte確保できている。(なぜ--idata_size 256ではないかは後で)

-- STC12C2052 ---
Internal RAM layout:       0 1 2 3 4 5 6 7 8 9 A B C D E F 0x00:|0|0|0|0|0|0|0|0|a|a|a|a|a|a|a|a| 0x10:|a|b|b|b|b|b|b|b|b|b|b|b|b|b|b|b| 0x20:|b|b|b|b|b|b|b|b|b|c|d|d|e|Q|Q|Q| 0x30:|Q|Q|Q|Q|Q|S|S|S|S|S|S|S|S|S|S|S| 0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0x80:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0x90:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xa0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xb0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xc0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xd0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xe0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S| 0xf0:| | | | | | | | | | | | | | | | | 0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute

 プログラム用のメモリが2kbyteしかないので、printf()は使えない。
↓実行結果

Stc12c2052_8q

 ところで、内蔵のR/C発振器でUARTを使おうとしたら、一発では動かない。ソースはそのままで動くと思っていた。

 通信速度の設定はTH1、TL1に設定する値で決まる。

   TH1 = TL1 = 256 - (クロック周波数 / 12 / 32 / 通信速度)

 SDCCのライブラリに autobaud()と言う関数がある。この関数を呼んでRxDにCR(0Dh)を送ると1byte(8bit)分の時間を測ってTH1、TL1レジスタを自動的に設定してくれるという便利な関数だ。 

 この関数を使ってみると、4,800bpsでは文字化けが多い、4,800bpsで動いていも次の日には文字化けが多くて使い物にならなくなる。確実に動かすには1,200bpsまで下げなければならない。

 TH1、TL1レジスタにセットされている値を表示させてみると大きい。 (TH1,TL1=FDh @4,800bps)
この値が大きいとズレが大きくなる。または正確なクロック周波数の発振器が必要だ。

 データーシートではクロック周波数は18.234MHzと書いてあることが多いので、R/C発振器の周波数も18.234MHzかと思っていた。 よく読むとそんなことは書いてない。

 データーシートに「R/C発振器の周波数を知る方法」が載っている。
電源ON直後、内蔵RAMのF8h~FBh、FCh~FFhを読むことで分かるのだそうだ。F8h~FBhに直前の設定値、FCh~FFhに工場出荷時の設定値が書かれている。この領域はRAMとして普通に使えるので当然データを書いたり読んだりできる。

 確認してみようと、この領域を読みだすと全て 00hだ。 (?_?

 SDCCはstart_up時にIDATA領域をクリアする。 (SDCC/lib/src/mcs51/crtclean.asm) つまり、IDATA領域の末尾の部分(F8h~FFh)も00hでクリアされているようだ。

 SDCCは --idata_loc と --idata_size オプションでIDATAのサイズと位置を指定できる。そこで、内蔵RAM(IDATA)は256byteあるけど --idata_size 240 とすると、F8h~FFhがクリアされなくなる。

 読んでみた。
Stc12c2052_freq  

 工場出荷時の設定値は 5,619776MHz、電源投入時の設定値は5,669,485MHzのようだ。

 よく見たら、stcgal.pyのメッセージで表示される値だ。



最近の投稿】【最近のCPUボード】【8031/8052 SBC】【2016の投稿】【2015の投稿

2017年9月24日 (日)

STC12C2052

aitendoSTCmicro Technologyのマイコンを売っている。 8051コアのCPUは特に珍しくもないのだが、前から気になっていたので買ってみた。

 どちらも、8051の上位コンパチでいろいろ拡張されている。

Stc12c2052_s

 STC12C2052は8051のSRAMを256byteにして、2kのフラッシュメモリを内蔵して、外部バス(AD0-7)とポート2(P2.0-7)を取って、SPI、I2C を付けたような感じだ。

 何と言っても安い100円。SBL(Serial Boot Loader)を持っているので、専用のプログラマがなくてもファームウェアを書くことができる。

Stc12le5604ad_s

 STC12LE5604ADは8051のSRAMを256byteにして、4kのフラッシュメモリ、256byteのRAMを内蔵して、外部バス(AD0-7)とポート2.6,P2.7を取って、A/D,SPI,PCA を付けたような感じだ。

 STC12C2052で遊んでみた。

コンパイラ

 SDCCが使えるので、F/W(ファームウエア)はCで書ける。
8051コンパチだからプロセッサオプションは -mmcs51でよい。include/mcs51/stc12.hに拡張されたレジスタが定義されている。

 コンパイルオプション、リンクオプションは↓

sdcc -o testuart.c.rel -I. -mmcs51 --model-small --no-xinit-opt -c test.c
sdcc -o test.ihx   -mmcs51 --model-small --code-loc 0x0000 --code-size 2048 \
     --idata-loc 0x0000  --iram-size 256  --out-fmt-ihx   test.c.rel obj

ライタ

 このMCUはPowerOnReset時にP1.0とP1.1がGNDに落ちていると、内蔵のISP monitorが走るので、ライタ用のハードウェアを用意しなくてもシリアル経由でプログラムを書き込むことができる。

 とはいえ、PC側のツールは必要だ。ネットを探すと、STCmicroからDLできるSTC-ISP.exeを使った説明が多いのだが、STC-ISP.exeは一部のワクチンソフトに引っかかるらしい。 他のツールを探すと stcgalが見付かった。

 stcgal (https://github.com/grigorig/stcgal)はpythonで書かれていてver3.2以降のpythonが必要らしい。Windowsでpythonを使うにはいくつか方法がある。

  1. Windows版のpythonを使う
  2. Cygwinのpythonを使う
  3. Bash On Windowsのpythonをつかう

1と2は使えることを確認した。3はシリアルデバイス(ttyS?)が見えない。

シリアルポート
 stcgalは/dev/ttyUSB0をデフォルトで使うので、ポートを指定しなければならない。
  1. Windows版のpythonは
    C:> python stcgal.py -p COMx 
    COMxはデバイスマネジャーで探す。
  2. Cgywinのpythonは
    $ python -p /dev/ttySx  
    ttySxは ls /dev で探す
ISP確認

 データシートの「1.6 STC12C2052AD series MCU Typical Application Circuit for ISP」にある回路図は、X'talとRST(Pin1)にCRが繋がっている。STC12C2052はR/C発信器を内蔵しているので、外付けX'talは接続しなくてよいようだ。また、RSTのCRも接続しなくてもよいようだ。

↓手前の青白ジャンパーはシリアル、奥側の白ジャンパー×2はP10,P11をGNDに接続、左側の赤ジャンパーは電源(未接続状態)

Stc12c2052_s

 ISPの手順は↓のとおり。

  1. GNDだけ接続。(VCCはまだ接続しない)
  2. STC12C2052のP3.0/RxD、P3.1/TxDをパソコンのシリアルに接
  3. STC12C2052のP1.0とP1.1をGNDに接続
  4. stcgal.py -p /dev/ttyS2  ←stcgal実行
  5. "Waiting for MCU, please cycle power:" ←が表示される。
  6. 電源投入(VCC接続)
  7. ↓のようなメッセージが表示される

$  ~/stcgal/stcgal.py  -p /dev/ttyS2
Waiting for MCU, please cycle power: done   
Protocol detected: stc12a
Target model:
  Name: STC12C2052
  Magic: F202
  Code flash: 2.0 KB
  EEPROM flash: 4.0 KB
Target frequency: 5.654 MHz
Target BSL version: 5.8D
Target options:
  low_voltage_reset=low
  clock_source=internal
  watchdog_por_enabled=False
  watchdog_stop_idle=False
  watchdog_prescale=256
  eeprom_erase_enabled=False
  bsl_pindetect_enabled=False
Disconnected!

 電源を接続しても "Waiting for MCU, please cycle power:" から進まないときは、一旦電源を切断して電源を接続しなおす。 タイミングがあるようで、1回で良い場合もあるし何度も接続しなおさなけば進まないいこともある。

Lチカ

書き込んだプログラムが動くか、Lチカで確認してみた。

$ stcgal -p /dev/ttyS2 testled.hex
Waiting for MCU, please cycle power: done
Protocol detected: stc12a
Target model:
  Name: STC12C2052
  Magic: F202
  Code flash: 2.0 KB
  EEPROM flash: 4.0 KB
Target frequency: 5.627 MHz
Target BSL version: 5.8D
Target options:
  low_voltage_reset=low
  clock_source=internal
  watchdog_por_enabled=False
  watchdog_stop_idle=False
  watchdog_prescale=256
  eeprom_erase_enabled=False
  bsl_pindetect_enabled=False
Loading flash: 807 bytes (Intel HEX)
Switching to 19200 baud: checking setting testing done 
Erasing 4 blocks: done
Writing 1024 bytes: ........ done
Setting options: done
Disconnected!

Lchika
↑リンク先に STC12C2052_Lchika.avi (1189.7K)


最近の投稿】【最近のCPUボード】【2016の投稿】【2015の投稿

2017年8月14日 (月)

赤外線送受信モジュール

 勉強会でaitendoの赤外線送受信モジュール[M1838-NEC-4P]の話題が出た。

 受信はできるけど送信できないらしい。興味がわいたので注文した。

 届く前に素性を調べてみた。aitendoには、このモジュールの素性は詳しく書かれていない。

 ぐーぐる先生に「ir receiver transmitter module」で尋ねてみると、同じ製品がたくさん引っかかる。

 aliexpressでは送料無料で $1.60で売っていたりする。

 使用されているIC(16pinSOP)の型番は見えないけれど、(http://www.valuehobby.com/nec-infrared-receiver-transmitter-encode-decode-module-for-arduino.html)には回路図もある。

 使われているICはSTC11F02Eらしい。(http://www.uctronics.com/infrared-remote-ir-decoder-encoding-transmitter-receiver-wireless-module-nec.html)の画像を見ると、 たしかに、STC11F02E と読める。

 STC11F02Eは STCが売ってるi8052コンパチのCPUらしく、専用チップではないらしい。
どおりでチップの情報が見つからないはずだ。

 このサイト(http://www.uctronics.com/download/U3107_Infrared_decoding_module.zip)にはマニュアルもあるのだが、「NOTE:There is only Chinese Manual.」だって。

 簡単なマニュアルだから、ぐーぐる先生に翻訳してもらうと大体わかる。

 NECフォーマット専用のようだから、家電を制御しようとすると動かないものがあるということか。

 バイナリデータが送信できるターミナルソフトがあればテストできそうだ。


最近の投稿

より以前の記事一覧