電子工作

5V単電源用のHD44780互換LCDキャラクタ液晶モジュールDMC16117Aを3.3Vで使う

課題: AVRで3.3V I2C動作の大気圧モジュールの値を読んで、LCDキャラクタ液晶に値を表示させたいが、5V用の液晶しか手元にない。昔のI2C仕様書にのっていたようなI2Cの5V-3.3Vレベル変換回路をNチャネルFET2個で組むにしても、その回路をつくる手間に加え、5Vから3.3Vを作る手間は別に生じる。3.3Vレギュレータはあと一本なので使わずとっておきたいし、前みたいに青色LEDをかわりに使うのもすでにやったので、ホビーという意味では別の方法を使いたい。
解決策: LCDの仕様書を見ると、コントローラの電圧範囲は4.5-5.5 Vと書いてあるが、HD44780の仕様書をみると3.3Vでの制御は可能。LCD用のコントラストの電圧だけおおよそ 4.0 V以上かかればよいらしい。1-2Vの負電圧をつくってコントラストピンに入れれば良い。負電圧はめんどくさそうだけど、どうするか、と調べてみたら、こちらのページにてチャージポンプを使った回路が紹介されていた。
http://elm-chan.org/docs/lcd/lcd3v_j.html

というわけで、AVRのPWMで50%デューティのクロックを作って、チャージポンプ回路をつくって入れてやりました。 -2.7Vが簡単にでました。手元に転がっていたダイオードはショットキーかどうかわからないが、-2.7 = -3.3 + 0.6であるので2本で ~0.6 Vの電圧降下であった。キャパシタは100nF, 1uFをつかいました。可変抵抗を入れなくても周波数で制御できそうでしたので、それを液晶のコントラストピンに直結しました。電圧が降下して文字が薄くなったので、周波数を83 kHz程度に調整すると、負荷ありで-1.7 Vとなり、良い感じの濃さで表示できました。


見難いけれどチャージポンプ回路。右は3.3Vで表示できた液晶

AVRでPWMをするのは簡単で、デューティを50%に固定してクロックとしてつかうのであれば割り込みなど必要なく、レジスタにPWMの設定をするだけでピンにクロックがでてきます。

PWM設定部分の抜粋です。


void pwm_init(void){
  OCR2A = 0x60;//TOP value, 83 kHz at 8 MHz
  OCR2B = (OCR2A >> 1);//duty 50%
  TCCR2A = 0b00100011;//OC2B PWM, WGM = 111
  TCCR2B = 0b00001001;//timer counter cont. reg., prescaler 1
}

LPS331使用 高精度大気圧モジュールのI2CでのONE_SHOT測定テスト

I2C通信chkに引き続き、大気圧ONE_SHOT測定のテストをしました。装置をactiveにして、one_shotの命令をし、測定が終わったかをSTATUS_REGで確認し、終わっていたらば大気圧を読みに行く、という流れです。
こちらも無事読み取りができました。

= 0x3f32b6 / 4096 とグーグル先生に聞いたら、1011.17 mbarだそうです。

コードはこちら。エラー処理はしてません。

続きを読む

LPS331使用 高精度大気圧モジュールとのI2Cでの通信テスト

秋月で、「LPS331使用 高精度大気圧モジュール」を650円で入手しましたので、そのテストです。大気圧モジュールの温度補償用として精度は±2 degCほどのようですが、温度計もついています。通信プロトコルとして、I2CとSPIに対応しています。5番ピンをプルアップするかしないかで、I2C/SPIの切り替えができます。AVRを使って、I2Cの通信の練習をしようと思いましたので、I2Cモードで通信のテストをしてみます。AVRにはTWIにハードウェア対応しているatmega328Pを使います。

LPS331APは3.3V動作です。まず、この前作ったUSB-AVRライタは5V供給ですので、3.3 Vレギュレータを入れて、3.3 Vにもジャンパピンで対応できるようにさくりと工作しました。

そして、適当にコーディングをしてから、PC5(SCL), PC4(SDA)をLPS331APへ配線します。WHO_AM_Iを連打するコードをつくり、ロジック・アナライザで動作チェックをしました。

一発目から無事、通信できていました。

ロジック・アナライザのカラフルな感じが好きです。


チップLED付きです。

コードはこちら。SCL, SDAを外部でプルアップしない場合は、PC5, PC4についてPORTCレジスタに1を入れてやれば、AVR内部でプルアップしてもらえます。ただAVRのプルアップは弱いので(抵抗が大きいので)100kbps以上の早い通信をしようとすると失敗しました。エラー処理はしていません。

続きを読む

ニキシー管時計のAVR-gccソースコード

本体は内部RCで1 MHzで動作し、時計用タイマは外部クロック端子につながっている時計用水晶振動子の32.768 kHzで動作させています。24時間制です。

時計合わせ用にボタン2つ(←と↑)がついています。←を押すと順に1分の位、10分の位、1時間の位、10時間の位が点滅して、その位の数字が調整できます。↑を押すと、点滅している桁の数字が1つ増えます。10秒なにもボタンを押さないと元にもどります。


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

#define TIMEOUT 10//config timeout
#define S_IDOL 0 //idol state
#define S_ONEMIN 1
#define S_TENMIN 2
#define S_ONEHOUR 3
#define S_TENHOUR 4

volatile uint8_t sec = 0;//0-60
volatile uint16_t min = 0;//0-60*(24 + 1)
volatile uint8_t confsec = 0;//0-10+alpha
volatile uint8_t state = S_IDOL;//state
volatile uint8_t red_last = 1;
volatile uint8_t white_last = 1;

int8_t display(uint16_t num);

ISR(TIMER2_OVF_vect){ //int. per 1 sec
  //count and set time
  sec++;
  if (sec >= 60){
    min++;
    sec = 0;
    if (min >= 60*24){
        min = 0;
    }
  }

  display((min / 60)*100 + min % 60);

  //config timeout
  confsec++;
  if(confsec > TIMEOUT){
    state = S_IDOL;
    confsec = 0;
  }

}


ISR(TIMER2_COMPA_vect){ //int. per 1 sec + 1/2
    //turn off -> unavailable 74141
    if(state == S_ONEMIN){
        //PB0,PD7
        PORTB |= (1 << PORTB0);
        PORTD |= (1 << PORTD7); //| (1 << PORTD6) | (1 << PORTD5));
    }else if(state == S_TENMIN){
        //PB1,2
        PORTB |= ((1 << PORTB1) | (1 << PORTB2));
    }else if(state == S_ONEHOUR){
        //PD0,1
        PORTD |= ((1 << PORTD0) | (1 << PORTD1));
    }else if(state == S_TENHOUR){
        //PC0,1
        PORTC |= ((1 << PORTC0) | (1 << PORTC1));
    }

}


ISR(PCINT1_vect){
  _delay_ms(10);
  uint8_t red_now = PINC & (1 << PINC4);
  uint8_t white_now = PINC & (1 << PINC5);

  if ((white_last != 0) & (white_now == 0)){//white pull down
    //shift state
    state++;
    if(state > S_TENHOUR){
        state = S_IDOL;
    }
    confsec = 0;
  }
  white_last = white_now;

  if ((red_last != 0) & (red_now == 0)){//red pull down
    confsec = 0;
    if(state == S_ONEMIN){
        min++;
        if(min % 10 == 0){
            min -= 10;
        }
    }else if(state == S_TENMIN){
        min += 10;
        if(min % 60 < 10){
            min -= 60;
        }
    }else if(state == S_ONEHOUR){
        min += 60;
        if((min / 60) / 10 < 2){
            if((min / 60) % 10 == 0){
                min -= 60*10;
            }
        }else{
            if((min / 60) % 10 == 4){
                min -= 60*4;
            }
        }
    }else if(state == S_TENHOUR){
        min += 60*10;
        if((min / 60) % 10 >= 4){
            if((min / 60) / 10 == 2){
                min -= 60*20;
            }
        }else{
            if((min / 60) / 10 == 3){
                min -= 60*30;
            }
        }
    }

    display((min / 60)*100 + min % 60);
  }
  red_last = red_now;
}

int8_t display(uint16_t num){
  uint8_t one = num % 10;
  uint8_t ten = (num / 10) % 10;
  uint8_t hund = (num / 100) % 10;
  uint8_t thou = (num / 1000) % 10;

  //4one DCBA -> PB0,PD7,6,5
  //3ten DCBA -> PB1,2,3,4
  //2hundDCBA -> PD0,1,2,3
  //1thouDCBA -> PC0,1,2,3
  PORTB = (((ten & 0b0001)?1:0) << 4)
    | (((ten & 0b0010)?1:0) << 3)
    | (((ten & 0b0100)?1:0) << 2)
    | (((ten & 0b1000)?1:0) << 1)
    | (((one & 0b1000)?1:0) << 0);

  PORTC = (((thou & 0b0001)?1:0) << 3)
    | (((thou & 0b0010)?1:0) << 2)
    | (((thou & 0b0100)?1:0) << 1)
    | (((thou & 0b1000)?1:0) << 0)
    | (1 << PORTC4)
    | (1 << PORTC5);

  PORTD = (((hund & 0b0001)?1:0) << 3)
    | (((hund & 0b0010)?1:0) << 2)
    | (((hund & 0b0100)?1:0) << 1)
    | (((hund & 0b1000)?1:0) << 0)
    | (((one & 0b0001)?1:0) << 5)
    | (((one & 0b0010)?1:0) << 6)
    | (((one & 0b0100)?1:0) << 7);

  return 0;
}

int8_t init(void){

//pc4(PCINT12) red, pc5(PCINT13) white
  DDRB = 0xFF;
  DDRC = 0b11001111;
  DDRD = 0xFF;
  PORTB = 0x00;
  PORTC = 0b00110000;
  PORTD = 0x00;


  ASSR   = 0b00100000; // use crystal.
  TCCR2B = 0b00000101; // 128 prescaler, timer on; 1 sec
  //TCCR2B = 0b00000100; // 64 prescaler, timer on; 1/2 sec
  //TCCR2B = 0b00000011; // 32 prescaler, timer on; 1/4 sec

  TIMSK2 |= (1 << TOIE2) | (1 << OCIE2A); // overflow and A interrupts on
  OCR2A = 0x80;

  //interrupt.
  PCICR |= (1 << PCIE1);
  PCMSK1 |= (1 << PCINT12) | (1 << PCINT13);//PCMSK1 = 0x30

  GTCCR  = 0b00000010; //timer reset
  sei(); // allow interrupts

  return 0;
}

int main(void){
  init();

  while(1){

  }
  return 0;
}