2009年06月13日

PIC12F629でエレキー

PIC12F629でエレキー

PIC12F629でエレキー

今回は、Microchip社の8ピンマイコン・PIC12F629でエレキー(アマチュア無線用)を作ってみました。
エレキープロジェクト第9ステージとなりました。

PIC12F629は、秋月電子さんでは80円で販売されている8ピンマイコンです。
前回紹介したPIC12F675に対してA/Dコンバータを内蔵していない分、50円ほど安く入手できます。

Webサイト上では、A/Dコンバータなしでの速度調整の解決策として

 ・CRの充放電回路を外付けして閾値までの時間計測
 ・ロータリーエンコード(約150円)を使用

などの方法が紹介されていますが、それぞれ

 ・ソフトウエアが若干ですが複雑になる
 ・可変抵抗に比べて値段が高い(今回の場合、マイコンより高くなる)

などのデメリットがあります。

今回は、内臓コンパレータを使用して速度調整することにしました。
内臓コンパレータのリファレンス電圧を可変して、擬似的なA/Dコンバータとします。
分解能は16段階(4bit)ですが、おそらく実用的レベルと思われます。

ソースコードでは、下記処理にあたります。

void volume() {
    if ((CMCON & 0x40) == 0x00) {
        ADCData = CntComp;
        ADCData += MAX_SPEED;
        CntComp = 0x0F;
    }
    else {
        CntComp --;
        if (CntComp == 0xFF)    CntComp = 0x0F;        // 0V~3.3V
    }
    VRCON &= ~0x0F;
    VRCON |= CntComp;
}

タイマイベントごとに、コンパレータのリファレンス電圧を1ビット-1して
次のイベント時に比較します。
理由は、リファレンス電圧可変時に最大10usec待ち時間が必要なため
次の周期まで比較処理を引き伸ばします。

その他、基本的な仕様、開発環境等は以前の記事「PIC12F675でエレキー」をご参照ください。

ポート配置と配線はこちらです。
LED出力     → GP5(負論理)
Buzzer出力   → GP4(Trバッファ付き)
Dot入力     → GP0(内部プルアップ抵抗)
Dash入力    → GP2(内部プルアップ抵抗)
速度調整     → GP1(VRコンパレータ入力)
回路図は下記の通りシンプルです。
PIC12F629でエレキー
VRと直列についている抵抗10kΩは、コンパレータの内部抵抗比に合わせています。
VRの値の半分の値の抵抗値を接続してください。

ソフトウエア機能としては下記を使用しています。
コンパレータ(-入力 + 内部リファレンス)
タイマ(0)割り込み
ポートの入出力/内臓プルアップ機能

PIC12F629のデータシート(英文)はこちら
 http://ww1.microchip.com/downloads/en/devicedoc/41190c.pdf

動作の様子はこちら
(16段階でも違和感はなさそうです。)



PIC12F675同様、SDCC環境でコンパイルしました。
コンパイル時のパラメータは下記です。
(pic12f629_elekey.c はソースファイル名)
> sdcc -V -c -mpic14 -p12f629 pic12f629_elekey.c
> gplink -I"c:\program files\SDCC\lib\pic" -w -r -o pic12f629_elekey pic12f629_elekey.o libsdcc.lib pic12f629.lib

ソースコードはこちらです。

#include <sdcc-lib.h>
#include <pic12f629.h>

//----------------------------------------------------------------------------
//    Define Code Parameter
//----------------------------------------------------------------------------
#define    PARA_ALL_OFF    0x00
#define    PARA_DOT_ON        0x01
#define    PARA_DOT_OFF    0x02
#define    PARA_DASH_ON    0x03
#define    PARA_DASH_OFF    0x04

#define    LIMIT_VALUE        0x03            // チャタリングフィルタ用

#define    MAX_SPEED        5
#define    MIN_SPEED        15


//----------------------------------------------------------------------------
//    Define User Memory
//----------------------------------------------------------------------------
unsigned int    ParaTime;
unsigned int    ADCData;

unsigned char    ParaCode;
unsigned char    CntDotOn;
unsigned char    CntDashOn;

unsigned char    CntComp;


//--------------------------------------------------------------------
//         Configuration Bits (pic12f629.h 内で定義)
//--------------------------------------------------------------------
//    _CPD_ON / _CPD_OFF                        : Data Code Protection
//    _CP_ON / _CP_OFF                        : Code Protection
//    _BODEN_ON / _BODEN_OFF                    : Brown-out(低電圧) Detect Enable
//    _MCLRE_ON / _MCLRE_OFF                    : GP3/MCLR pin function select
//    _PWRTE_OFF / _PWRTE_ON                    : Power-up Timer Enable
//    _WDT_ON / _WDT_OFF                        : WatchDogタイマ設定
//    _LP_OSC / _XT_OSC / _HS_OSC / _EC_OSC    : 200kHz以下外部/4MHz以下外部/4MHz以上外部/内部RC
//    _INTRC_OSC_NOCLKOUT / _INTRC_OSC_CLKOUT    : INTOSC oscillator: CLKOUT function
//    _EXTRC_OSC_NOCLKOUT / _EXTRC_OSC_CLKOUT    : EC: I/O function
//--------------------------------------------------------------------

int at 0x2007 __config = _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT;


//----------------------------------------------------------------------------
// 初期化
//----------------------------------------------------------------------------
void Init() {
    CMCON = 0x04;            // コンパレータ使用
    VRCON = 0xA0;            // powered on / Low range

    TRISIO = 0x0F;            // GP0~3:Input ,GP4~5:Output
    WPU = 0x0D;                // GP0/2/3 キー入力端子はプルアップ抵抗
    GPIO = 0x00;

    //OPTION_REG = 0x81;    // プリスケーラ: TMR0/4     0.256*4msec
    OPTION_REG = 0x03;        // プルアップ許可、プリスケーラ:TMR0/16(0.256*16msec)
    TMR0 = 0x00;
    T0IE = 1;                // TMR0割り込み許可
}

//----------------------------------------------------------------------------
// ボリューム電圧値(コンパレータ使用)
//----------------------------------------------------------------------------
//    bit 7 VREN: CVREF Enable bit
//        1 = CVREF circuit powered on
//        0 = CVREF circuit powered down, no IDD drain
//    bit 6 Unimplemented: Read as '0'
//    bit 5 VRR: CVREF Range Selection bit
//        1 = Low range
//        0 = High range
//    bit 4 Unimplemented: Read as '0'
//    bit 3-0 VR3:VR0: CVREF value selection 0 d" VR [3:0] d" 15
//        When VRR = 1: CVREF = (VR3:VR0 / 24) * VDD
//        When VRR = 0: CVREF = VDD/4 + (VR3:VR0 / 32) * VDD
//        Settling Time 10μs(MAX)
//----------------------------------------------------------------------------
void volume() {
    if ((CMCON & 0x40) == 0x00) {
        ADCData = CntComp;
        ADCData += MAX_SPEED;
        CntComp = 0x0F;
    }
    else {
        CntComp --;
        if (CntComp == 0xFF)    CntComp = 0x0F;        // 0V~3.3V
    }
    VRCON &= ~0x0F;
    VRCON |= CntComp;
}

//----------------------------------------------------------------------------
//    LEDを点灯する処理
//----------------------------------------------------------------------------
void LED_ON() {
    GPIO &= ~0x20;
}

//----------------------------------------------------------------------------
//    LEDを消灯する処理
//----------------------------------------------------------------------------
void LED_OFF() {
    GPIO |= 0x20;
}

//----------------------------------------------------------------------------
//    Buzzer On する処理
//----------------------------------------------------------------------------
void BUZZER_ON() {
    GPIO |= 0x10;
}

//----------------------------------------------------------------------------
//    Buzzer Off する処理
//----------------------------------------------------------------------------
void BUZZER_OFF() {
    GPIO &= ~0x10;
}

//----------------------------------------------------------------------------
//    Dotポートチェック
//----------------------------------------------------------------------------
char ChkDotPort() {
    if ((GPIO & 0x01) == 0)    return 1;
    return 0;
}

//----------------------------------------------------------------------------
//    Dashポートチェック
//----------------------------------------------------------------------------
char ChkDashPort() {
    if ((GPIO & 0x04) == 0)    return 1;
    return 0;
}

//----------------------------------------------------------------------------
//    Set Output port
//----------------------------------------------------------------------------
void SetOutput()
{
    if ((ParaCode == PARA_DASH_ON) || (ParaCode == PARA_DOT_ON)) {
        LED_ON();
        BUZZER_ON();
    }
    else {
        LED_OFF();
        BUZZER_OFF();
    }
}

//----------------------------------------------------------------------------
//    Check Input port
//----------------------------------------------------------------------------
void ChkInputPort()
{
    // for Dot
    if (ParaCode != PARA_DOT_ON) {
        if (CntDotOn < LIMIT_VALUE) {
            if (ChkDotPort())            //    Dotポートチェック
                CntDotOn ++;
            else
                CntDotOn = 0;
        }
    }
   
    // for Dash
    if (ParaCode != PARA_DASH_ON) {
        if (CntDashOn < LIMIT_VALUE) {
            if (ChkDashPort())            //    Dashポートチェック
                CntDashOn ++;
            else
                CntDashOn = 0;
        }
    }
}

//----------------------------------------------------------------------------
//    Set Parameter Dash
//----------------------------------------------------------------------------
char SetParaDash()
{
    if (CntDashOn >= LIMIT_VALUE) {
        CntDashOn = 0;
        ParaCode = PARA_DASH_ON;
        ParaTime = (ADCData * 3);
        return 1;
    }
    return 0;
}

//----------------------------------------------------------------------------
//    Set Parameter Dot
//----------------------------------------------------------------------------
char SetParaDot()
{
    if (CntDotOn >= LIMIT_VALUE) {
        CntDotOn = 0;
        ParaCode = PARA_DOT_ON;
        ParaTime = ADCData;
        return 1;
    }
    return 0;
}


//----------------------------------------------------------------------------
//    キー処理メイン関数
//----------------------------------------------------------------------------
void key_sequence(void)
{
    volume();

    if (ParaTime != 0) {
        ParaTime --;
        if (ParaTime == 0) {
            if (ParaCode == PARA_DOT_ON) {
                ParaCode = PARA_DOT_OFF;
                ParaTime = ADCData;
            }
            else if (ParaCode == PARA_DASH_ON) {
                ParaCode = PARA_DASH_OFF;
                ParaTime = ADCData;
            }
            else if (ParaCode == PARA_DOT_OFF) {
                if (!SetParaDash())
                    ParaCode = PARA_ALL_OFF;
            }
            else if (ParaCode == PARA_DASH_OFF) {
                if (!SetParaDot())
                    ParaCode = PARA_ALL_OFF;
            }
            else {
                ParaCode = PARA_ALL_OFF;
            }
            SetOutput();
        }
    }

    ChkInputPort();
    if (ParaCode == PARA_ALL_OFF) {
        if (!SetParaDot())
            SetParaDash();
    }
    SetOutput();
}


//----------------------------------------------------------------------------
// 割り込み(タイマ割り込みのみ使用)
//----------------------------------------------------------------------------
static void Inntrpt(void) interrupt 0
{
    if (T0IF) {                    // TMR0割り込み?
        T0IF = 0;                // 割り込みフラグクリア
       
        key_sequence();            // キー処理メイン関数
    }
}


//----------------------------------------------------------------------------
//    Main関数
//----------------------------------------------------------------------------
main()
{
    Init();                        // システムレジスタ初期化

    // ユーザメモリの初期化
    CntDashOn = 0;
    CntDotOn = 0;
    ADCData = MIN_SPEED;
    ParaTime = 0;
    ParaCode = PARA_ALL_OFF;
    LED_OFF();
    BUZZER_OFF();

    GIE = 1;                    // 全割り込み許可
   
    while (1) {
        ;
    }
}



スクイズ機能で不用意にキーがメモリされてしまい、速度を上げると余計な符号が出る不具合があります。



同じカテゴリー(マイコン開発)の記事

この記事へのコメント
80円でマイコンってそれだけでもうれしくなっちゃいますね。
勉強会はArduinoよりもこのマイコンのほうがいいかも。


>>・CRの充放電回路を外付けして閾値までの時間計測
これって Apple][ のジョイステックの方式と同じですね。
Posted by ビーコン-菅原洋一 at 2009年06月13日 04:17
ビーコン-菅原さん

こんいちは。

マイコンは安いですが、少し工作時間が必要となりますね。

Apple][ はそう言うシステムを利用していたのですね。
回路的には古典的な方法なのでいろいろなところで使われていると思います。
Posted by 宇都宮 at 2009年06月13日 08:40
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

削除
PIC12F629でエレキー
    コメント(2)