エントリー

AVRtiny13Aでつくるシリーズ1 電圧計

  • 2016/03/29 07:05
  • カテゴリー:make:

 AVRtiny13Aを使った工作,第一弾は電圧計です。


20160329082048.jpg


 この工作のテーマですが,複雑に入り組んだ現代社会に鋭いメスを入れ,さまざまな謎や疑問を徹底的に究明するようなものではなく,たった50円で手に入る一番安いマイコン,AVRtiny13Aを使って,いろいろなものを作ってみようという物です。

 改めてAVRtiny13Aを眺めてみると,

・最大20MHzで動作,内蔵クロックを使う場合は最大9.6MHz
・1kバイトのプログラムROM
・64バイトのRAM
・64バイトのEEPROM
・10ビットA-Dコンバータ
・8ビットタイマ/カウンタ
・コンパレータ
・最大6本のGPIO
・ちゃんとCで書ける
・安い。秋月価格で1個50円

 てな8ピンのマイコンです。なにせ8ピンですからね,配線数が少なくて,作るのがとっても楽ちんなのですよ。ピン数が少ないマイコンってこんなに楽だとは思いませんでした。

 ワシの若い頃はな,CPUが40ピンもあってな,GPIOも外付けで40ピン,カウンタ/タイマも28ピン・・・そもそもメモリも32kByteで28ピンじゃからな・・・配線だけで頭がおかしくなりそうじゃった・・・

 ・・・その時作ったZ80ボードは捨てられずにまだとってあります。

 それでも,CPUもTTLで自作していた時代から考えると随分楽になったんだろうとは思いますが,細いリード線を切り,両端を剥いてはんだめっきをして,ハンダ付けをしていくという単純作業をしていると,まるで写経をしているかのように,心が澄み渡り,世の中の煩わしい物がすべて後ろに流れていくような感覚に陥ります。ええ,単純作業大好きですよ。

 本題に戻ると,こんな小さなマイコンでどこまで出来るかという話です。GPIOについても,1つはRESET端子と排他ですから,これを使うと安いライタでのプログラミング(ISP)も出来なくなります。

 それ以外でも,GPIOは必ずなにか他の機能と兼用になっています。でも,この数本というGPIOの数と1kバイトのプログラムというのは,やってみると案外バランスが良くて,特にCで書いた大雑把なプログラムだと,1kバイトくらいでちょうどGPIOを数本動かす程度で一杯になります。

 そうなると64バイトのRAMなんてのはユーザーが意識するRAMというより,スタックや変数といった,いわばコンパイラが使うスクラッチパッド,と言う感じです。これは悪い意味ではなく,むしろCでプログラムを書くことが出来る,ギリギリのマイコンに仕上がっているという話です。堂々とCで書きましょう。

 これが,ちょっと上位のtiny2313だったり,あるいはmegaシリーズだったりすると,ペリフェラルも豊富ですから,やりたいことは大体できます。でも,すべての機能を試し,いつでも使えるように鍛えておくには少々骨が折れます。使い切った感じがしないというのは,懐の深さでもありますが,底なし沼の深さでもあるのです。

 てことでtiny13Aです。

 手始めに10ビットのA-Dコンバータで遊んでみましょう。

 13Aの前世代品にはなかったものだそうで,こんなちっこいマイコンにマルチプレクサ付きの10bitA-Dコンバータなんかいるんかいなと思うのですが,なかなかどうして,これがあるといろいろつぶしが利きます。

 もちろん,単純に電圧を測ることにも使えますが,ボリュームを使ってデータのエントリーに使うことも出来ますし,抵抗分割でスイッチを8個くらいぶら下げる事もできます。GPIOが不足するtiny13Aでも,結構便利に使えそうです。

 まず,LCDはデジットで売っていたポケベル用と思われる200円の8桁14セグメントのもの,A-Dの基準電圧は内蔵の1.1Vで,1chだけ使います。

 14セグメントLCD,なかなかいいですよね。

20160329082049.jpg


//
//    VoltMeter for tiny13
//        LCD : Digit 14seg type
//        Clock : 1.2MHz internal
//

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#define F_CPU 1200000UL


#define LE    _BV(PB0)    // _LE
#define DIN    _BV(PB1)    // DIN
#define SCK    _BV(PB2)    // SCK


unsigned char tx_buf[18];
unsigned char digit_buf[8];


const PROGMEM int seg_pat[] = {
//    0        1        2        3        4        5        6        7        8        9
    0x003f, 0x1200, 0x00db, 0x00cf, 0x00e6, 0x00ed, 0x00fd, 0x1401, 0x00ff, 0x00ef,
//    SPC        A        M        V        -        E        ,
    0x0000, 0x2486, 0x0536, 0x2430, 0x00c0, 0x00f9, 0x2000};

const PROGMEM unsigned char bit_pos0[14] = {
     86, 85, 84, 83, 82, 80, 81,  1,  0,  6,  5,  4,  3,  2};

void LCD_Disp(void);
void LCD_Clear(void);
void LCD_Convert(void);


int main(void)
{

    unsigned char i;
    int data;

    DDRB = 0b11101111;        // PB4 = Input
    DIDR0 = 0b00010000;        //ADC2 = analog
    PORTB = 0b00000000;        // PB = All Low

    ADMUX = 0b01000010;        // VREF=1.1V, Right-ADJ(10bit), ADC2
    ADCSRA = 0b10000100;    // ADEN=1, 1/16=75kHz


    digit_buf[7] = 13;    //    V
    digit_buf[4] = 16;    //    ,
    digit_buf[1] = 10;    //    SPC
    digit_buf[0] = 10;    //    SPC


    while(1){

        data =0;
        for (i=0 ; i<30 ; i++){

        ADCSRA |=_BV(ADSC);
        loop_until_bit_is_set(ADCSRA,ADSC);

        _delay_ms(3);
        data += ADC;        // 10bit = ADC
        }

        data /= 30;

        digit_buf[6] = data % 10;
        data /= 10;

        digit_buf[5] = data % 10;
        data /= 10;

        digit_buf[3] = data % 10;

        if (data < 10){
            digit_buf[2] = 10;
        }else{
            digit_buf[2] = 1;
        }

        LCD_Clear();
        LCD_Convert();
        LCD_Disp();   

    }


}
void LCD_Disp(void)
{
    int i, j;
    unsigned char k;

    for (i=142 ; i>=0 ; i--){

        PORTB &= ~SCK;    //SCK = Low

        j = i/8;
        k = i%8;

        if ((tx_buf[j] & (1<<k)) != 0)
        {
            PORTB |= DIN;
        }
        else
        {
            PORTB &= ~DIN;
        }
        _delay_us(1);
        PORTB |= SCK;    //SCK =High

        _delay_us(1);
    }
    _delay_us(1);

    PORTB |= LE;
    _delay_us(1);

    PORTB &= ~LE;

}


void LCD_Clear(void)
{
    unsigned char i;

    for (i=0 ; i<=17 ; i++) {
            tx_buf[i] = 0;
    }   
}

void LCD_Convert(void)
{
    int p, r, s;
    int pattern, tx_bit_pos;
    unsigned char digit_num, seg_num;
   
    for (digit_num=0 ; digit_num<=7 ; digit_num++){

        pattern = pgm_read_word(&seg_pat[digit_buf[digit_num]]);

        for (seg_num=0 ; seg_num<=13 ; seg_num++){

            p = pattern & 1; // LSB of pattern
            pattern /= 2; // shift right

            tx_bit_pos = pgm_read_byte(&bit_pos0[seg_num]);
            tx_bit_pos += digit_num * 7;

            r = tx_bit_pos / 8;
            s = (1<<(tx_bit_pos % 8)) * p;
            tx_buf[r] = ((tx_buf[r])& ~s) | s;
        }   
    }
}

 大した事はやっていません。プログラムの大半がLCDのドライブ用です。A-D変換については,8ビットだけ使う方法と10ビットを使う方法があって,8ビットの方が楽だという話があるのと,サンプリング周期が短いと値がふらつくので,周期をゆっくりにするなり,いくつかの平均を取るなり,工夫が必要という事です。

 


 私もよく言うのですが,デジタルテスターで更新速度が遅い物は安価な物という考え方があります。確かに高精度なものは更新周期も短く,レスポンスがいいのですが,一方で値がふらつき,読みにくくなります。

 もちろん,こうしたふらつきの大きさや周期も重要な情報となるのですが,それが面倒な場合も多いのです。アナログテスターは,針の行き来に時間がかかるため,細かい変動は出てこないわけで,これが一種のローパスフィルタになっていたわけですが,これをデジタルでも再現しないと,使い心地の良いものにはならないのです。

 ということで,他に書くこともないので,次いってみましょう。次は,LMT01温度センサです。

ページ移動

ユーティリティ

2020年05月

- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -

検索

エントリー検索フォーム
キーワード

ユーザー

新着画像

新着エントリー

過去ログ

Feed