PIC
食わず嫌いだったのだと思います。マイコンチップの開発環境が非常に良くなっているという話は、だいぶ前から聞かされていたのですが、何となく手を出さないままでした。2005年1月、必要に迫られて、タイマーカウンターを作ったところ、意外に簡単で驚いてしまいました。それでも、アセンブラはまだ手強い。これを読んだ方が再現できるような形での情報公開を目指してみます。
PIC マイコン PIC-BASIC 編
EOS Kiss 用タイマーカウンター
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言語編
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 型の処理が異様に遅い。こんなものなのかなあ?
割り込みの処理が今ひとつわからなくて、いろいろ情報を探し回っています。
明るさが変えられる懐中電灯
下記の仕様で作ってみました。
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);
}
}
}
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)
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
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
言語としては、こちらのほうが自然な感じを受けます。