2008年9月26日金曜日

Tiny26Lで電圧計(3)


電圧計のソースコードです。Cで書いてあります。下の「+expand source」というリンクをクリックすると表示されます。私はWinAVRを導入して、開発環境AVR Studioからビルドしています。

Tiny26Lへのプログラムの書き込みはAVRISPmkIIを使っています。ヒューズ設定をデフォルトのまま「内蔵RC発振=1MHz」として使えばOKです。特にいじる必要はありません。ただし、一度設定をいじったヒューズをデフォルトの状態に戻そうとした場合には注意が必要です。AVR Studio 4.13というバージョンではTiny26Lの構成ファイルにバグがあり、ヒューズの設定をプルダウンメニューから正しく選ぶことができないのです。その場合にはデータシートとにらめっこして、直接ヒューズの下位バイトに数値を入力してください。今回の場合はLow Byte=0xE1です。上のイメージはその時のウィンドウのスナップショットです。

電圧計 main.c

#define F_CPU 1000000L /* システムクロック=1 MHz(内蔵RC発振) */
#define COM_MAX 3 /* LEDのcommon端子の数 */
#define CALIB_REAL_MV 5080L /* リアル校正電圧[mV] */
#define CALIB_ADC_MV 2570L /* ADC校正電圧[mV](リアル校正電圧の約半分で入ってくる) */
#define LED_REFRESH_MS 15 /* LEDのリフレッシュレート[ms] */
#define LUMINOUS_PERSENT 30 /* LED輝度[%] */
#define ADC_REFRESH_MS 200 /* ADCの更新周期[ms] */
#define ADC_REFRESH (ADC_REFRESH_MS / LED_REFRESH_MS) /* ADCの更新周期[LEDリフレッシュ回数] */

#define COM_SWITCH_MS (LED_REFRESH_MS / COM_MAX) /* COM端子1本当たりの時間[ms] */
#define LED_ON_MS (COM_SWITCH_MS * LUMINOUS_PERSENT / 100) /* LEDがONしている時間[ms] */
#define LED_OFF_MS (COM_SWITCH_MS - LED_ON_MS) /* LEDがOFFしている時間[ms] */

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

//
// Globals
//
uint16_t CurVolt;
uint8_t SegRAM[3];
uint8_t CurCom = 0; /* Current COM Number 0, 1, 2, 0, 1, 2, ... */
uint8_t SegFont[10] PROGMEM = {
//_abcdefg
0b10000001, /* 0 */
0b11001111, /* 1 */
0b10010010, /* 2 */
0b10000110, /* 3 */
0b11001100, /* 4 */
0b10100100, /* 5 */
0b10100000, /* 6 */
0b10001101, /* 7 */
0b10000000, /* 8 */
0b10000100, /* 9 */
};

//
// Prototypes
//
void wdt_off(void);
void init_io(void);
void adc_cont_start(void);
void put_digit(void);
void print_volt(void);

//
// MAIN LOOP
//
int main(void)
{
wdt_off(); /* ウォッチドッグタイマーをOFF */
init_io(); /* I/Oの初期化 */
adc_cont_start(); /* ADCを連続変換モードでスタート */

uint8_t adcref = 0; /* ADCのリフレッシュカウンタ */

while(1) {
if (adcref == 0 && (ADCSR & _BV(ADIF))) {
ADCSR |= _BV(ADIF); /* ADCの読み込み */
CurVolt = ADCW; /* 現在電圧の更新 */
}
print_volt(); /* 電圧の表示 */

if (++CurCom >= COM_MAX) { /* 各カウンタの回転 */
CurCom = 0;
if (++adcref >= ADC_REFRESH)
adcref = 0;
}

_delay_ms(LED_ON_MS); /* 輝度調整:ONの時間 */
PORTB |= 0b00111000; /* 全消灯 */
_delay_ms(LED_OFF_MS); /* 輝度調整:OFFの時間 */
}

return 0;
}

//
// Functions
//
void wdt_off(void)
{
cli();
wdt_reset();
MCUSR &= ~(1 << WDRF);
WDTCR |= (1 << WDCE) | (1 << WDE);
WDTCR = 0x00;
//sei();
}

void init_io(void)
{
PORTA = 0xff;
DDRA = 0xff;

PORTB = 0b10111111;
DDRB = 0b00111000;

ADMUX = 0x29; /* REFS[1:0]/ADLAR/MUX[4:0] = 00101001 */
/* AVCCピン基準電圧、左揃え、シングルエンドCH9 */
ADCSR = 0x87; /* ADEN/ADSC/ADATE/ADIF/ADIE/ADPS[2:0] = 10000111 */
/* 完了割り込み禁止、クロック128分周 */
}

void adc_cont_start(void)
{
ADCSR |= _BV(ADSC) + _BV(ADFR);
}

void put_digit(void)
{
uint8_t pb, pa;

pb = PORTB | 0b00111000;
pb &= ~(0b00001000 << CurCom);
pa = SegRAM[CurCom];

PORTB = pb; /* COM端子のスイッチ */
PORTA = pa; /* フォントパターンの出力 */
}

void print_volt(void)
{
uint32_t r;
uint16_t p;

if (CurCom == 0) {
r = CurVolt;
//r = r * VCC_MV / 1000;
r = r * (CALIB_REAL_MV * CALIB_REAL_MV / CALIB_ADC_MV) / 1000L;
p = r >> 16;
if (p > 9)
p = 9;
SegRAM[0] = pgm_read_byte(&SegFont[p]) & 0b01111111; /* 最上位桁+小数点 */

r -= p * 65536L;
r *= 10;
p = r >> 16;
SegRAM[1] = pgm_read_byte(&SegFont[p]); /* 中位桁 */

r -= p * 65536L;
r *= 10;
p = r >> 16;
SegRAM[2] = pgm_read_byte(&SegFont[p]); /* 最下位桁 */

// r -= p * 65536L;
// r *= 10;
}

put_digit(); /* SegRAM[]の内容(フォントパターン)で表示 */
}

0 件のコメント: