ホーム

 PIC 食わず嫌いだったのだと思います。マイコンチップの開発環境が非常に良くなっているという話は、だいぶ前から聞かされていたのですが、何となく手を出さないままでした。2005年1月、必要に迫られて、タイマーカウンターを作ったところ、意外に簡単で驚いてしまいました。それでも、アセンブラはまだ手強い。これを読んだ方が再現できるような形での情報公開を目指してみます。

PIC マイコン PIC-BASIC 編

EOS Kiss 用タイマーカウンター

Rimg1055s.jpg (70630 バイト) 2005年1月25日 天体撮影では、多くの枚数を撮影して、コンポジット行います。そのために、観望地ではシャッター ON/OFF をを繰り返す単純労働が待っています。これを自動化するのが、タイマーカウンターです。予めセットした露出時間で、セットした回数だけシャッターを切ってくれるという装置です。

 同種の装置は、キヤノンに EOS 用の純正品がありますが、Kissに接続するためには、コネクターを改造する必要があること、また、1 万円以上と高価なことから、作ってみました。曲がりなりにも自作派の看板を掲げているのに、作らなくてど〜する、という悪魔のささやきが、最も強い動機でしょうか。

 同じ機能の物を作っても面白くないので、バッテリー電圧と温度を表示するようにしてみました。この PIC マイコンは、8ch 10bit の A/D コンバータを搭載していますので、わずかな外付け部品で実現可能です。

 アナログ、デジタル回路の組合せでも設計できますが、今日日、1チップマイコンでしょ! と粋がって、PIC を検討しましたが、アセンブラは自信が無く、C もコンパイラが高いし初期設定をみると、あまりCのメリットがない。というわけで、秋月電子で販売している PIC-BASIC というのを使用しました。開発用ベースボードをそのまま使用したので、PIC にしては 5,400 円と少々高価になりました。他に小さな部品をいくつか使用しています。

 作業時間は、画像のようなベアボードの状態でソフトも含めて、約 10 時間です。
 コネクターは、ステレオ用のφ2.5ミニジャックがそのまま使用できます。カメラ本体とは、ホトカプラを使って絶縁しました。これらの付加部品は、液晶ディスプレイ下のユニバーサル基板で配線しています。消費電流は 、動作時で約 20mA です。
 純正のタイマーカウンターに無い機能として、バッテリー電圧と温度を表示する機能も組み込みました。上の画像は、動作中のものです。2つ点灯している LED は、シャッターの状態を表しています。残り時間及び回数が 48 秒、2 回であること、電源電圧が6.67V、外気温が 16 ℃、いずれかのスイッチを押せばストップする事を示しています。電圧及び気温の換算は、調整中です。
 下記は、PIC-BASIC のプログラムです。動けば ok の殴り書きですので、きれいなソースではないです。
 温度のデータ取り込みの分解能が、1℃で2ビットぐらいの分解能しかないため、10回の平均を計算して表示しています。また、PIC-BASICでは、負の数字が使えないので、零下表示は少し複雑なことをやりました。
 液晶表示は、バックライト付きに変更しています。

'デジカメシャッターコントロール PIC-BASIC
' Copyright (C) ssato. 2005 All right reserved. 2005.2.14c版
'EOS Kiss D で、t秒露出、c回の露出を繰り返す。露出後は、10秒間の待ち時間を設定
'SW1:RB0に接続(既定)スタート SWはONで接地、4.7kΩプルアップ
'同様に、SW2:RC0 機能なし SW3:RC1 回数設定 SW4:RC2 時間設定
'RA0 電源電圧監視 入力電圧を、46k、22kΩで分割して入力。
'RA1 温度入力 LM35DZ のOUT端子 10mV/℃
'RA2 LM35DZ のGND端子、VSSに対し、ダイオード2個でフローティング
'RD0 半押し出力 RD1 シャッター出力 いずれもホトカプラ出力

Dim t, c, t1,s, w, v, vv, temp1,temp2, tempa, tempav, tempp As Byte't1:仮時間定数、w:ループ用仮定数

Initlcd            '液晶ディスプレイ初期設定
Homelcd            'カーソルをホームポジション
tris_rd=0        'RDを出力モードに設定
rd=&Hff         '全LED消灯

in_md:            '入力モード

    Setpos 0,0             'カーソルを左上へ
    Putlcd t," sec"        '露出時間と単位(sec)表示
    Setpos 9,0             'カーソルを、上中央へ
    Putlcd c," カイ"         '回数と単位表示

If vv=0 Then
    vv=100
    Gosub vandt
    Setpos 11,1
    Putlcd "TC*S "

Endif
    vv=vv-1

If rb.Bit0=0 Then        'SW1が押されたときの処理
    Clearlcd
    If t=0 Then             '無効な設定の場合、戻る
    Goto in_md
    Endif
    If c=0 Then
    Goto in_md
    Endif
    t1=t
    Gosub open             'シャッターオープン
    Goto out_md             '出力モードへ移行
Endif

If rc.Bit1=0 Then         'SW3が押されたときの処理、回数を増やす
c=c+1
Sleep 200            'チャッタリング防止、カウントアップ速度制限
Endif

If rc.Bit2=0 Then        'SW4が押されたときの処理、30秒ずつ増やす
t=t+30                 '30秒ずつ
Sleep 200
Endif
Goto in_md            '入力モードをループする。

out_md:'出力モード

'    Clearlcd        '液晶ディスプレイ設定
    Setpos 0,0
    Putlcd t1," sec "
    Setpos 9,0
    Putlcd c," カイ "

Gosub vandt

For w=0 To 5            '1周で1秒
Sleep 113
Gosub stoploop            'STOPを検知するルーチンを監視
Next w

t1=t1-1                 '表示を1秒ずつ減らす。

    If t1=0 Then             'タイムアップ時の処理
        Gosub close             'シャッタークローズ
        c=c-1
        t1=t
            If c=0 Then
                Clearlcd
                Goto in_md
            Endif
        Gosub wait         '画像保存時間を待つ
        Gosub open         'シャッターオープン
    Endif

Goto out_md

open:        'シャッターオープンサブルーチン------------------
    rd=254        'シャッター半押し
    Sleep 100    ' 0.1秒待つ
    rd=252        'シャッターオープン
Return

close:        'シャッタークローズ-------------------------------
    rd=255
Return

wait:        '画像保存時間を10秒待つ--------------------------
For w=0 To 10
    Setpos 0,1
    Putlcd "Wait 10 sec ", w, " "
    Gosub stoploop
    Sleep 900
Next w
Return


stoploop:    'ストップキーの監視ルーチン-------------------

If rb.Bit0=0 Then
    Gosub close
    Sleep 1000        'すぐにスタートへ移行しないように待つ
    Goto in_md
Endif

If rc.Bit0=0 Then
    Gosub close
    Goto in_md
Endif

If rc.Bit1=0 Then
    Gosub close
    Goto in_md
Endif

If rc.Bit2=0 Then
    Gosub close
    Goto in_md
Endif
Return


vandt:    '電圧及び温度測定ルーチン---------------------

    Adc 0,0,v    '電源電圧入力
    v=v*71/5    '入力電圧の換算

    tempav=1000

For tempp=0 To 9    '温度の入力
    Adc 1,0,temp1
    Adc 2,0,temp2
    tempav=tempav+temp1-temp2
Next

    temp1=tempav/10+900

    If temp1<1000 Then    '零下の判別
        tempp=2
        temp1=1000-temp1
    Endif
    If temp1>1000 Then
        tempp=1
        temp1=temp1-1000
    Endif

    Setpos 0,1        '電圧と温度表示
    Putlcd v/1000,".", v Mod 1000
    Setpos 4,1
    If tempp=2 Then Putlcd "V -", temp1*20/43,"C STOP "
    If tempp=1 Then Putlcd "V +",temp1*20/43,"C STOP "
Return

PIC マイコン C言語編

Rimg1019s.jpg (64475 バイト)

 2005年2月1日 PIC-BASIC が意外にあっさり出来たので、調子づいて、PICライター、Cコンパイラ etc を秋月電子で買い込んできました。早速組み立てて、さらに実験用ボードを作りました。
 C コンパイラは、Grich RC 社の 1kB までの限定版です。今回のターゲットは、PIC16F84Aでプログラム領域が1kBしかないので、限定版で充分です。このコンパイラに関する資料は全くないと申し上げても良いのではと思います。一般的な C の書き方をしてみて、通るか通らないか、そんな試行錯誤を繰り返しています。基本的には、IOポートは変数のように扱い、これらに値を与えるような書き方をします。ようやく、スイッチと LED の入出力が出来たところ。

← PIC ライター(上) と、実験用ボード(下)
 開発環境のハードウエアは、これらとパソコン、シリアルケーブル、電源だけです。たったこれだけで開発が可能になってしまうのですから、簡便です。

 

 開発に使用しているソフトウエアは、PIC ライターの書き込みソフト(左上)、Cコンパイラ(左下)、エディタ WZ(右下) です。 →

 PIC はアセンブラでしょ、 とは思うのですが、高級言語の便利さには勝てないです。今回は、Grich C という秋月電子で2000円で売っている C コンパイラを使いました。コンパイラとしては、お金さえ出せば、もっと便利なコンパイラがあります。アマチュアがちょっと使ってみるには、こういうタイニーなコンパイラの選択が現実なわけで、、、。これは、Visual-C++ のように巨大化して、機能を見つけるのに時間のかかるソフトとは対極にあるというところでしょうか。
 なおこの GrichC は、Windows2000 で動作させると、ntvdm.exe 及び wowexec.exe を呼び出し、その上で動作します。この2つのプログラムは、環境によっては若干の不具合を起こすことがあります。私の環境では、終了する際にしばらくの間、Windows の操作レスポンスが異常に低下します。

pic_disp.gif (11251 バイト)

Grich C

 この C は、どこが、ANSI 準拠なの? っていうぐらい言語の機能が貧弱で制限も多く、おまけに Instruction set すら掲載されていない説明書で、大丈夫かなと思いましたが、やはり苦労させられました。
 動作のおかしな点、注意すべき点は、見つけ次第列挙していきます。

1. unsigned int では、i-- が出来ない。
 どうも引き算全般が不可能なようです。 long では、ok。

2. 剰余 % の動作が?
 1ずつインクリメントする数値では、a%3 の計算結果は、0,1,2,0,1,2,,,, となるはずですが、0,1,2,0,1,0 となっているようです。理由は不明。

3.  基本的に MS-DOS アプリですので、ファイル名は、8文字まで。

4. long 型の処理が異様に遅い。こんなものなのかなあ?

 割り込みの処理が今ひとつわからなくて、いろいろ情報を探し回っています。

明るさが変えられる懐中電灯

 下記の仕様で作ってみました。Rimg1053s.jpg (37167 バイト)

1. 赤と強力な白色光、両方の光を出せる懐中電灯。
2. 光の強さは、テレビリモコンの音量調整のように、任意に可変可能。
3. スイッチを OFF して次回 ON するときは、前回の設定と同じ光を出すことが可能。
4. パトライト機能として、7色に変化するイルミネーションライト機能付

配線は、PIC、LED、スイッチ、抵抗、電源用のパスコンだけ。

バラックの動画ファイル  約 5MB、avi ファイル アセンブラファイル blink_20.asm HEXファイル blink_20.hex

 明るさの変更は、ソフト的に 出力パルス幅を可変し、PWM 制御する事により行っています。スイッチはポーリング処理です。
 ソースリストはお見せできないほど汚いものになりました。暗黙のキャスト、ループからの goto での抜け出し、スタック節約のスパゲッティプログラム、初心者がこういうプログラムを書いたら、先輩風を吹かしながら小一時間、説教してしまいます。そんなわけで、Cのソースコード公開は、少し整理してからにさせて下さい。
結線は、下記の通り。IC そのものの消費電力は、約 1mA です。
残った課題は、もう少し明るくしたいのと、複雑怪奇なスイッチ操作を単純化したいこと。明るさは、Aポートも並列に接続すれば、電流を2倍にできます。

ターゲット PIC16F84
MCLR_ :Reset スイッチ 20kΩプルアップでGND間にSW
RB1: Sel スイッチ 白色、赤色、消灯切り替え。 GND間にSW。プルアップ不要。以下同様。
RB2: Dec スイッチ 白色、赤色点灯時に減光。消灯時に押すと、スリープとなり、消費電流が1μA以下となる。復帰は、Reset。
RB3: Inc スイッチ 白色、赤色点灯時に増光。消灯時に押すと7色点灯。
RB5: LED 赤。47Ω直列
RB6: LED 緑。47Ω直列
RB7: LED 青。47Ω直列
セラロック 4MHz

CCS-C (PCM)

 趣味ですから、Grich-C に関数を足していって拡張して使い込むという方法もあったのですが、変数も貧弱で資料も少ないなど何かと使い勝手が悪いので、CCS-C を購入しました。個人輸入で送料も含め $149 です。注文の仕方は極めて簡単で、下記から必要事項を記入するだけです。4日で到着しました。
http://www.ccsinfo.com/cgi-bin/order.cgi (あえてリンクを外しています。)
 インストールに際して、折角ですから、MPLAB との統合環境にしました。
 float 変数がある、math.h が付属しており、三角関数、対数などが使える、など普通の C 言語に近い感じで使えます。しかしながら、こちらが予期せぬ動作をすることもあります。コンパイル時のエラー以外には、エラーを探るのに技術がいるなど PIC 特有の問題もあり、学校で教えているような C 言語よりも少しだけ技術を必要とするように感じます。

 下記は、動作確認がわりに作った、対数出力のプログラムです。レンジ切り替えのある計測器などで、全レンジに渡って、出力を一つのアナログ信号で得たい場合があります。そこで、アナログ入力と桁切り替え信号を入力し、桁 +LOG という形で、LOG 出力を得ようというものです。一桁が約 0.5V になっており、約 9 桁 (4bit-BCD) に対応しています。出力は、電圧で出力しています。
 キャスティングが目立ちますが、エラーを潰していったらこうなったという結果で、正しいやり方かどうかまだ良くわかっていません。桁が切り替わったとき、出力は大きく変動しますので、0.5 秒待ってから取り込むようにしています。この辺りは計測関係のちょっとしたテクニックです。
 ターゲットの PIC は、PIC16F819 です。PIC の解説本では、PIC16F84 を扱うことが圧倒的に多いのですが、819 は、同じピン配置で、10bitAD コンバータ、PWM、発振器まで搭載して、しかも値段も安いです。84 と同じピン配置と書いてしまいましたが、リセット端子や発振器の端子まで I/O に回せますので、18pin のうち、電源と接地以外の 16ピン、2byte の I/O が確保できます。デジカメ用タイマーカウンターもこれ一つで機能させることも可能だと思います。
 16F819 を焼き込むには、秋月電子のライターの場合、Ver4 にバージョンアップし、1箇所だけ配線を変更することが必要です。

 それにしても、とんでもない時代になってしまいました。アナログ回路のテクニックについては、だいぶ磨きをかけてきたつもりですが、PIC を初めとするデジタル技術をここまで簡単に扱えるようになってしまうと、大電力、超高周波など一部を除いて、アナログ回路はただ単にレベルを揃えるだけのインターフェースという位置づけになってしまいそうです。

#if defined(__PCM__)
#include <16F819.h>
#device ADC=10
#include <MATH.H>
#fuses INTRC, NOLVP, NOWDT
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);

//Copyright (C) ssato. 2005 All right reserved.
//入力電圧を LOG 変換する。
//17pin RA0 入力端子 0-5V
//16pin 出力FB入力 2pinと10k,100uFのLPFで接続。出力端子 0-5V
//2pin RA3 疑似PWM出力 出力端子 0-5V
//6pin RB0 LSB 桁信号入力 負論理
//7pin PB1 |
//8pin RB2 |
//9pin RB3 MSB

main()
{
long reading, outs, digit; //input, out_sense, digit
int i;
float lout; //log output

setup_adc_ports(RA0_RA1_RA3_ANALOG); //RA0,RA1をアナログ入力
setup_adc(ADC_CLOCK_DIV_32); //AD変換クロック設定
port_b_pullups(TRUE); //桁信号入力プルアップ

digit=(long)(0xff-input_b()); //桁信号入力 負→正変換

while(1){ //無限ループ
if(digit != (long)(0xff-input_b())){ //桁信号入力
delay_ms(500); //変化があるなら、0.5秒待ってから入力
digit=(long)(0xff-input_b());
}

set_adc_channel(0); //信号を入力
delay_us(35);
reading = read_adc();
if(reading<10)reading=10; //LOG出力の−過大を防止

lout=100*log10((float)reading/100);
if(lout<0 && digit==0)lout=0; //負出力を防止
reading=(long)lout+100*digit; //信号と桁設定を加算

for(i=0;i<9;i++){ //D-A変換ルーチン
        set_adc_channel(1);
        delay_us(35);
        outs = read_adc();
    if(reading<outs)output_low(PIN_A3);
    else output_high(PIN_A3);
}

}
}

Rimg1103s.jpg (36971 バイト)C 言語で LCD 表示させる

 やっと液晶ディスプレイに数字と文字を表示させることが出来ました。わかっている人には、な〜んだというプログラムでしょうが、意外に時間がかかりました。液晶ディスプレイは、秋月電子のSC1602BS-B(-S0-GS-K) という型番のものです。M1632 互換とかいてあり、これがおそらく業界標準みたいなものなのでしょうか。ソースにゴミが残っているかもしれませんが、御容赦下さい。後閑哲也著 PIC活用ガイドブック 技術評論社などでは、書き込みをキャラクターとコマンドに分けて、サブルーチンを作っているのですが、今回は、でれ〜っと書いてしまいました。みっともないという御批判は甘んじて受けます。疲れた。

関数の説明

init_lcd() 液晶ディスプレイの初期化
lcd_int() int変数の表示
lcd_char() 文字の表示
lcd_pos(a,b) a行b列へカーソルを移動

#if defined(__PCM__)
#include <16F819.h>
//Copyright (C) ssato. 2005 All right reserved.
#fuses INTRC, NOLVP, NOWDT
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);
//B0-D4, B1-D5, B2-D6, B3-D7, B4-RS, B5-E

init_lcd()
{
delay_ms(100);
output_b(0x03);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(5);
output_b(0x03);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x03);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//from 8 to 4bit
output_b(0x02);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//fuction set
output_b(0x02);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x0e);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//Dispaly on/off cont
output_b(0x00);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x08);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//Dispaly on/off cont
output_b(0x00);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x0d);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//mode set
output_b(0x00);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x06);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);

//delete
output_b(0x00);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(0x01);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1000);
}

lcd_n(int b)
{
output_b(0x13);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
output_b(b+0x10);
output_high(PIN_B5);
output_low(PIN_B5);
delay_ms(1);
}

lcd_int(int a)
{
int b;

b=a/100;
lcd_n(b);
a=a-b*100;
b=a/10;
lcd_n(b);
a=a-b*10;
lcd_n(a);
}

lcd_char(char a)
{
    char b;
    b=a>>4;
    a=a & 0x0f;

    output_b(b+0x10);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);
    output_b(a+0x10);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);
}

lcd_pos(int l, int c){
    output_b(0x00); //cursol at home
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);
    output_b(0x02);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(2);

if(l==2){    //line 1 or 2
    output_b(0x0c);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);
    output_b(0x00);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);    }

for(;c>0;c--){    //column 0-15
    output_b(0x01);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);
    output_b(0x04);
    output_high(PIN_B5);
    output_low(PIN_B5);
    delay_ms(1);    }
}

main()
{
while(1){
init_lcd();

lcd_int(255);
lcd_char(' ');
lcd_int(1);

lcd_pos(2,3);

lcd_char('s');
lcd_char('s');
lcd_char('a');
lcd_char('t');
lcd_char('o');

delay_ms(5000);
}
}

PWM を試す

 LED を点灯させるとき、通常は抵抗で電流制限をかけますが、インダクタンス(コイル)を直列に接続しスイッチング方式で電流制限すれば、高効率にすることが出来ます。例えば、抵抗による電流制限で 12V で LED を点灯させようとしたとき、消費電力のほとんどは抵抗で消費されてしまいます。スイッチング動作は、対数出力で使った D/A と同じ方法で実現させるとその動作は、非常に遅くなり、結果的に大きなインダクタンスが必要になります。高いスイッチング周波数を得るには、PIC の PWM 機能を使うのが最適です。注意すべき事は、main() 関数が終了すると、PWM 出力も終了すること。そのため、永久ループを作る必要があります。おそらく、空の永久ループでも動作すると思いますが、、、、
 デューティ比を1にしても、「明るさが変えられる懐中電灯」ほどには暗くなりません。OFF 時でもコイル、LED、ダイオードのループで電流が持続するためです。

#if defined(__PCM__)
#include <16F819.h>
#device ADC=10
#fuses INTRC, NOLVP, NOWDT
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);
//Copyright (C) ssato. 2005 All right reserved.
//B2 にMOS-FETのゲート接続 さらに10kΩでプルダウン
//ソースは接地
//ドレインは、コイル、LED を直列に接続、それにダイオードを並列接続

main()
{
long duty;//デューティ比設定

output_low(PIN_B2);// FETにONされるとLEDが破壊する。
set_tris_b(0x80);// 出力にすることは必須。
output_low(PIN_B2);//念のため

setup_ccp1(CCP_PWM); //モード切り替え
setup_timer_2(T2_DIV_BY_1, 0xFF,1);//設定
duty=1;

while(1){

for(duty=1;duty <100 ;duty++){
set_pwm1_duty(duty);
delay_ms(10);
}

EIA232C を試す(2006/07/25)

RIMG0096s.jpg (39096 バイト) RS232C のほうがまだ一般的な名称でしょう。下記のようなプログラムを作ってみました。

1. アナログ信号を取り込み、その値を0〜1023で EIA232C に出力
2. 同時に PWM 出力。

 プログラムはあっけないほど簡単で、初期設定に数行足しただけで、あとは printf( ) 文で出力を記述するだけです。16F819 は、USART を搭載していませんので、どの出力ピンを使っても負荷は変わらないはずです。EIA232C の調歩同期はかなりシビアでして、prntf( ) 文の中身を長くすると、文字化け発生率が急増しました。ボーレートは、多少高速なほうがエラーが少ないみたいです。あまり精度のない内部クロックを使っていますので、内部クロックでどの程度のエラーが生じるかも検証の対象でした。下記の出力を WindowsXP 付属のハイパーターミナルで受信した限りでは、エラーはほとんどありませんでした。なお、printf( ) 文の中身を倍ぐらいの長さにすると、正常に伝送される確率が、10%以下になります。途中で同期がずれてきてしまうのでしょう。おそらく、パソコンのクロックもあまり正確ではないでしょう。そんなわけで、長い文章を伝送するときは、文字の間に時間をとる意味で、printf( ) 文をいくつも並べたほうがよいと思われます。delay (遅延時間)は適当に設定していますが、EIA232C の送出データに適当な間隔が開くように設定する必要があります。ボーレートを速くすることは、データ送出間隔を開けることにも効きます。
 右の画像は、実験の様子です。左下が、EIA232Cのデータ送出を確認しているハンディタイプのオシロスコープ、右下は、実験基板で右が 16F819、左が EIA232Cドライバー(ADM3202) です。オシロスコープの画面では、データの間隔が開いていることが確認できます。
 なお、このソースリストをコンパイルすると、無限ループのところで、Warning が出ますが、無視して構いません。。

ソースリスト----------------

#if defined(__PCM__)
#include <16F819.h>
#device ADC=10
#fuses INTRC, NOLVP, NOWDT
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);
#use rs232(BAUD = 19200, XMIT = PIN_B6, RCV = PIN_B7)

//Copyright (C) ssato. 2006 All right reserved.
// 入力電圧をpwm変換する。pwm_opt.c CCS-Cコンパイラ
//17pin RA0 入力端子 0-5V
//8pin RB2 出力 RCフィルターを付けて出力
// Vdd 14, 4(_MCLR), GND(Vss) 5pin
// RS232C input B7 output B6

void main(void)
{
long reading;

setup_adc_ports(RA0_RA1_RA3_ANALOG); //RA0,RA1をアナログ入力
setup_adc(ADC_CLOCK_DIV_32); //AD変換クロック設定

set_tris_b(0x80);// 出力にすることは必須。
setup_ccp1(CCP_PWM); //モード切り替え
setup_timer_2(T2_DIV_BY_1, 0xFF,1);//設定

while(1){ //無限ループ
    set_adc_channel(0); //入力ポートをA0にセット
    delay_us(6000);
    reading=read_adc();
    printf("%lu\r",reading);  //RS232C出力
    set_pwm1_duty(reading);  //PWM出力
    }
}

複数のアナログ入力

複数のアナログ入力をするときに、set_adc_channel( ) の引数はどうなるのか、明確に書いてる資料が見つからなかったので試しました。ポート番号を入れればよいことがわかります。CCSCの最近のバージョンでは、"#if defined(__PCM__)"がうまく通らないことがあるようです。理由は深く追求していません。

//#if defined(__PCM__)
#include <16F819.h>
#device ADC=10
//#include <MATH.H>
#fuses INTRC, NOLVP, NOWDT, NOMCLR
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);

//Copyright (C) ssato. 2007 All right reserved.

void main(void)
{

setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_32);
set_tris_b(0xf0);
set_tris_a(0x8b);

setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1,0xff,1);

while(1){

    set_adc_channel(0);
    delay_us(60);
    set_pwm1_duty(read_adc());

    delay_ms(5000);

    set_adc_channel(1);
    delay_us(60);
    set_pwm1_duty(read_adc());
   
    delay_ms(5000);
   
    set_adc_channel(3);
    delay_us(60);
    set_pwm1_duty(read_adc());
   
    delay_ms(5000);

}
}

2進入力 → 7セグメント2桁ダイナミック表示 2012年8月20日更新

 初心者に役に立つかなと思いアップしておきます。
 RA0-7から、RA0-LSB、正論理で2進数を入力すると、RBに接続した7セグメント表示器に 0〜99 までの数字を表示するというものです。100〜255の数値が入力されると、10の位が、"E"表示になります。エラー表示は、"EE"とか点滅するとか、もう少し派手にしてやった方が良いと思いますが手を抜きました。
 電源を入れると、最初に、0〜9、Eを各桁ごとに表示します。ここも"見映え"を改善できますが、デバッグ初期において、ソフトが悪いのか、ハードが悪いのかという切り分けをしなければならないとき、ソフトはなるべく安全確実な方法で記述したほうが良いので、こんな方法をとってみました。ただ、表示の問題をつぶせていれば、こんな羅列は意味無いので、ここを最後に見映え良く表示できるように書き換えることはできます。ここも、その最後の一手間で手を抜いたという事です。
 16F819の使い方として気をつけなければならないのは、

#fuses INTRC_IO

の記述です。INTRC ですと RA6 pin からの入力が出来ません。このことは、16F819 のデータシートから、INTRCについて2つのモードがあり RA6、7 の入力が出来ないモードがあることがわかり、16F819.h の中から、INTRC、INTRC_IO のいずれかがそれらに該当することは類推できるのですが、詳細に調べたわけではないですが、簡単には説明が見つけられないです。ただ、_IO と書いてあるので、おそらく INTRC_IO が RA6 を入出力可能にする設定であることは想像できます。
実際に、ここを INTRC にしてやると、RA6 の入力が出来なくなり、63 以上の数字が表示できません。
また、RA7から入力は可能ですが、0〜99表示ですので意味無しです。
 余談ですが、2進数 → 10進数変換ってどうやるんだ? と数学の教科書をめくるようなことをすると混乱しますね。99 までであれば、
  1. 10 で割って整数部分を取れば、これが10の位の数値
  2.  10 で割ったときの剰余が、1の位の数値
です。では 100 の位まで表示させたいときはどうするか? 100 で割った時の剰余を使いますが、これ以上は説明の必要はないですね。但し、16F819 ではピン数が足りなくてこのままでは表示できないです。00〜FF 表示にするのは簡単ですが、、、
 ハードの方では、10 の位の MOS-FET が ON するのか? と不安になるかも知れませんが、LED の暗電流で充分に ON 可能なVgsが得られます。

#include <16F819.h>
#fuses INTRC_IO, NOLVP, NOWDT, NOMCLR
#use delay(clock=8000000)
setup_oscillator(OSC_8MHZ);
//Copyright (C) ssato. 2012 All right reserved.
//Binary -> 7segment
//RA0-7 input binary number
//RB0:a RB1:b RB2:c RB3:d RB4:e RB5:f RB6:g RB7 H:1digit L:10digit

int8 bin_7seg(int8 n){
switch (n){
    case 0:return 63;break;         //a,b,c,d,e,f
    case 1:return 6;break;          //b,c
    case 2:return 91;break;          //a,b,d,e,g,
    case 3:return 79;break;         //a,b,c,d,g
    case 4:return 102;break;    //b,c,f,g
    case 5:return 109;break;    //a,c,d,f,g
    case 6:return 125;break;    //a,c,d,e,f,g
    case 7:return 7;break;         //a,b,c
    case 8:return 127;break;    //all
    case 9:return 103;break;    //a,b,c,f,g,
    default:return 121;break;//E:a,d,e,f,g
} //switch end
} //bin_7seg end


void main(void){
int8 nm; //binary input,
int1 flag;

setup_adc_ports(NO_ANALOGS); setup_adc(ADC_OFF); // ADC OFF
set_tris_a(0xff); // all A is input
set_tris_b(0x00); // all B is output
flag=0;

output_b(191);delay_ms(200);//test pattern 0-9,E
output_b(134);delay_ms(200);output_b(219);delay_ms(200);output_b(207);delay_ms(200);output_b(230);delay_ms(200);
output_b(237);delay_ms(200);output_b(253);delay_ms(200);output_b(135);delay_ms(200);output_b(255);delay_ms(200);
output_b(231);delay_ms(200);output_b(249);delay_ms(200);

output_b(63);delay_ms(200);output_b(6);delay_ms(200);output_b(91);delay_ms(200);output_b(79);delay_ms(200);
output_b(102);delay_ms(200);output_b(109);delay_ms(200);output_b(125);delay_ms(200);output_b(7);delay_ms(200);
output_b(127);delay_ms(200);output_b(103);delay_ms(200);output_b(121);delay_ms(200);

while(1){
  nm=input_a();

    if (flag == 0) //10digit
        output_b(bin_7seg(nm / 10));
    if (flag == 1) //1digit
        output_b((bin_7seg(nm % 10))+ 128);
  
  flag++;
  delay_ms(5);
  } //while end
} // main end

WO_nu_disp.jpg (35946 バイト)

PIC の参考書

CCS-C ベースの参考書

後閑哲也 著 改訂版 電子工作のための PIC16F 活用ガイドブック 技術評論社 2004年初版

後閑哲也 著 電子工作のための PIC 活用ガイドブック 技術評論社 2000年初版
 上記2冊は、ほとんど同じ内容の本です。完成度は前者のほうがダントツに良い。後者も、解説しているのは、16F だけなので、前者のほうだけで充分でしょう。

後閑哲也 著 C 言語による PIC プログラミング入門 技術評論社 2002年
 前半文は C 言語の基本について、後半が組み込み関数とその使い方、リアルタイムOSの構築についての説明が、それぞれ 1/4 ずつになっています。必要に応じて、使い方をさらっとチェックするという感じの使い方でしょうか。

PICC-Lite の参考書

中尾 真治 著 C 言語ではじめる PIC マイコン オーム社 2005年
 HIGH-TECH 社の PICC-Lite は、無料で入手可能な C コンパイラです。2005 年時点では、PICC-Lite に付いて書かれている、おそらく唯一の日本語解説書だと思います。PICC-Lite は、なかなか魅力的なのですが、対応しているチップが極めて限定されているため、発展性がないのが弱みです。全機種に対応している製品版は、CCS-C よりかなり高価です。 IO制御などの関数は、CCS-C とかなり異なりますが、C 言語としては、こちらのほうが自然な感じを受けます。

Copyright (C) 2019 ssato. All Rights Reserved 2021/12/31