2007年から2008年にかけて,トランジスタ技術2007年8月号に付録されたdsPICマイコン基板を使った「dsPICマイコン基板 デザイン・コンテスト」が行われました.
ここでは,このコンテストで敢闘賞を受賞した「セラミック・ヒータはんだゴテ温度コントローラ」を紹介します.
●プログラミング
プログラムのほうは,アセンブラの経験は少々あるものの,C言語でプログラムを書くのは初めてでしたが,制御式とか演算条件がそのまま表記できますし,係数の変更が非常に簡単なので,基本的な動作のデバッグが終わった後の制御系の調整と仕上げが楽しいものとなりました.
とにかくMICROCHIP社が公開しているMPLAB-IDEの機能が非常に強力で,C言語環境でのコンパイラ,シミュレータなどが充実していて,ICEなしでもこの程度の規模のプログラムはデバッグ可能です.ただし、トラ技提供のプログラム・ローダは必須で,筆者も40~50回は書き換えを行ったでしょうか.
PICを含めて近ごろのワンチップ・マイコンはROM/RAM容量やプロセッサの速度が昔と比較にならないほど進歩していますから,コンパイラに よる多少のプログラム容量や動作時間の増加は,高性能のハードウェアがすべてカバーしてくれますし,数式の記述でバグることがありませんから、制御系プロ グラムへのC言語適応は現代の成り行きなのだと実感しました.
今回のプログラムは約1300ステップで,30Hzインターバルの1回の動作時間はPWM最小デューティ時(On→Offまでの時間実測)に待ち時間を含めて400μsくらいですので,そのほか計算時間をいれても500μs以下と思われます.
リスト1(IronControl_10.c)という名前のブロックがメイン・ルーチンです.ファンクションごとに整理して記述し,できるだけコ メントを記述しましたので,フローチャートと比べながら何を行っている部分か解読してください.C言語に関して筆者は素人ですから,慣れた人が書けばもっ ときれいな記述ができると思います.
//--------------------------------------------------------------------------
// IronControl_10.c
// Soldering Iron Temperature Control Program
//
// Written by Yuzo Nakagawa 2007 Sep.26
// Last edit 2008 Jul.28
//
//--------------------------------------------------------------------------
#include <p30f2012.h>
#include <dsp.h>// Coefficent for 20W/25W
char watt;
unsigned long coef1;
unsigned int coef2;// Switch Status Check Work
unsigned int flagT1; //Timer-1 Interrupt Done Flag
unsigned int down_sw; // Down Switch
unsigned int up_sw; //Up switch
unsigned int disp_onesec; //1 Sec Counter
unsigned int disp_req; //Display Update Requestunsigned int c_mode; //Constant Power Mode
// Moving Average Work Bufferunsigned int array1[16] __attribute__((space(xmemory), aligned(32)));
unsigned int array_index;// Servo work
unsigned int resultADC; //Dimension:ADC
unsigned int total_ma; //ditto
unsigned int ave_now; //ditto
unsigned int now_tmp; //Dimension:degC
unsigned int target_tmp; //ditto
unsigned long target; //Dimension:ADC
signed long delta; //ditto
signed long integ_delta; //ditto
signed long current_ratio; //Dimension:Timer-2 count
//----------------------------------------------------------------------
// TIMER1 interrupt handlervoid __attribute__ ((__interrupt__)) _T1Interrupt(void)
{
IFS0bits.T1IF =0; // clear interrupt
flagT1 =1; // Set T1_Done flag
}//----------------------------------------------------------------------
//----------------------------------------------------------------------
// TIMER2 interrupt handlervoid __attribute__ ((__interrupt__)) _T2Interrupt(void)
{
IFS0bits.T2IF =0; // clear interrupt
T2CONbits.TON=0; // Stop T2
PORTFbits.RF6=1; //Power Off
}//----------------------------------------------------------------------
//----------------------------------------------------------------------void main(void) {
// IO definition
CNEN1=0; // Confirm ChangeNotice Off
CNEN2=0;
CNPU1=0;
CNPU2=0;TRISBbits.TRISB8=1; // RB8 input (ADC Analogue Input)
TRISBbits.TRISB9=1; // RB9 input (Up Switch)
TRISFbits.TRISF4=1; // RF4 input (Down Switch)
TRISFbits.TRISF6=0; // RF6 Output (-IronPower On)
PORTFbits.RF6=1; // Initialize default as Power Off
TRISCbits.TRISC15=1; // RC15 Input (+20W/-25W Switch)TRISCbits.TRISC13=0; // RC13 Output (Original Card LED)
PORTCbits.RC13=0; // Initialize LED
INTCON2=0xF000; // use AIVT (Alternate Interrupt Vector Table)// Read 20/25 switch and set appropriate parameters
if (PORTCbits.RC15==1)
{
watt='0'; //20W Mode
coef1=195768;
coef2=110;
}
else
{
watt='5'; //25W Mode
coef1=263063;
coef2=107;
}// Initialize MovingAverage Buffer as 100 degC
int iii;
for (iii=0; iii<16; iii++)
{
array1[iii]=coef1/(100+coef2);
}array_index=0;
total_ma=16*coef1/(100+coef2);
target_tmp=400; //Default target
integ_delta=0; //Initial value of Integrator as zero
flagT1=0;disp_req=0;
up_sw=0x07; //off->off->off
down_sw=0x07; //off->off->off//Initialize ADC
ADPCFG = 0x02FF; // Set only RB8 to Analogue Input
ADCSSL = 0x0000; // Scan mode = 0
ADCHS = 0x0008; // RB8 = AD input
ADCON3 = 0x1F20; // Auto Sample Time=31*Tad=31*0.561=17.39uS
// Tad=internal (32+1)/2*Tcy=16.5*34n=561nS
ADCON2 = 0x0000; // ADCON2 = Default (One Sampl&Coversion per period)
ADCON1 = 0x80E0; // ADC enable, Sampling Time = Auto// Initialize LCD and display default parameters
Init_Display();// PWM by Base Interval of 33mS using TIMER1 interrupt
// Start Timer-1 as the base Clock of 30Hz free run
// PR1= 3800 // Fosc=29.48MHz/256/3800 = 30.3HzT1CON=0x0000; // clear TIMER1 control register
TMR1 =0x0000; // clear TIMER1 counter register
PR1 =3800; // set TIMER1 period register
IFS0 =0x0000; // clear all interrupt
T1CON=0x8030; // start TIMER1 (prescaler 1/256)
IEC0 =0x0048; // enable TIMER1/2 interrupt
IPC0 =0x6000; // Set T1 priority as 6T2CON = 0x0030; // Set Timer2 prescaler 1/256
while(1) { // forever loop
while (flagT1 !=1) {} // Wait Timer-1 Interval
flagT1=0; // Clear Interval Flag// AD read
T2CONbits.TON=0; // Stop Timer2 avoid overrun
PORTFbits.RF6=0; // Power On
for (iii=0; iii<2000; iii++){} // Wait the current to be stabilizedADCON1bits.DONE=0; // Confirm DONE clear
ADCON1bits.SAMP=1; // Start Sampling & Conversion
while (ADCON1bits.DONE != 1){} // Wait Sample and Conversion
resultADC=ADCBUF0;// Initialize Timer-2 by Current Ratio
if (current_ratio > 38)
{
TMR2 =0x0000; // clear Timer2 counter register
PR2= current_ratio;
T2CONbits.TON = 1; // Start T2
}
else
PORTFbits.RF6=1; // Power Off if Ratio < 1% of Max// Moving Average
total_ma = total_ma + resultADC;
total_ma = total_ma - array1[array_index];
array1[array_index] = resultADC;
ave_now =total_ma / 16;
array_index++;
if (array_index==16)
array_index=0;// Servo calculation
target = coef1 / (target_tmp+coef2);
delta = (long)ave_now - target; // Polarity change here
if (delta < 50)
{
if (delta > -20)
{
integ_delta = integ_delta + delta/4;
current_ratio = delta * 80 + integ_delta;
}
else
{
current_ratio = 0 ;
integ_delta = 0;
}
}
else
{
current_ratio = delta * 80 ;
integ_delta = 0;
}
if (current_ratio > 3800)
current_ratio = 3780; //adjust within 2 degits
else if (current_ratio < 0)
current_ratio = 0;
// ave_now => now_tmpif (ave_now < coef2 * 2)
now_tmp = 999; //may be NoIron connected
else
now_tmp = coef1/ave_now -coef2;// Read switch status
up_sw=(up_sw<<1)+PORTBbits.RB9; //get the new status of UP
up_sw=up_sw & 0x0007; //watch three times
if (up_sw==0x0004) //Yes, it is Off->On->On
{
target_tmp=target_tmp+10;
disp_req=1;
if (target_tmp>450) //limitter of 450 degC
target_tmp=450;
}
else
{
down_sw=(down_sw<<1)+PORTFbits.RF4; //Same as UP Switch
down_sw=down_sw & 0x0007;
if (down_sw==0x0004)
{
target_tmp=target_tmp-10;
disp_req=1;
if (target_tmp<200)
target_tmp=200;
}
}// Check 1 sec or Target change
disp_onesec ++;
if (disp_onesec == 30) //If One Second
{
disp_onesec = 0;
disp_req = 1;
PORTCbits.RC13 = ~PORTCbits.RC13; // Original Card LED (red<->green)
}
if (disp_req == 1)
{
Update_Display();
disp_req=0;
}} // Interval Loop end
} // Main End//-----------End of File-------------------------------------------------------
最後の部分の1秒間を数えているルーチンで,トラ技基板のLEDを赤緑反転させて,ハングしていないか一目でわかるようにしています.デバッグでLCDが動き出すまで,これは大いに役立ちました.
筆者が使用した参考資料は,dsPICを操作する部分に関しては,トラ技とMICROCHIP社の資料など,またC言語の内部変数の処理や変数定義などの一般論に関しては,1980年代に発刊された「プログラミング言語C」(カーニハン,リッチー著)を使用しました.
●実装
ハード
ウェアは手持ちのジャンク部品などで高圧パワー部とPIC/LCD部に分けて組み,端材で適当に作り上げた木製ケースに組み込みました.とくにシールドも
施していませんので、EMI的には盛大にノイズを放出していてAMラジオとは同居ができないと思われます.実機の写真を参照してください.
また,GNDに対する考慮として,片側GND接地の日本の2線式ACを直接ブリッジ整流したDCは平均的にGNDレベルとなりますから,はんだ付 け作業中のCMOS半導体などをコテ先の電位で劣化させることはないと考えられますが,環境が許せばコテ自体をGNDに落とすことができる3線式のACプ ラグを推奨したいと思います.
日本でもはんだゴテのように静電気に敏感な工具とか,高圧を扱う電子レンジとか,漏電・感電の心配な洗濯機とか温水便座などは3線式のACプラグ で機器きょう体を直接接地してしまえる環境が安全ではないでしょうか.デバッグ中にPCとかオシロを接続する際には,感電と漏電防止のためAC100Vの アイソレーション(分離絶縁)が必要です.
●感想
性能向上
に関しての改造案は,PIC自体がピン数の少ないパッケージ対応のシリアルI/O前提の1チップ・マイコンですから,LCDなどもシリ-パラ変換を外付けす
るほうがプログラムは簡単になりますし,接続するI/O
PIN数が減ってA-Dコンバータに外部基準電圧を使用できるなど,システム全体の精度を改善する方法がまだまだ残っています.
現状ハードウェアではA-Dコンバータの基準電圧は内部5Vを使用しており,400℃設定時のA-Dコンバータ出力が400カウントくらいですか ら,センス抵抗の値を上げるか,LCDを4ビット接続に変更してピンを空け,A-Dコンバータの基準電圧を外部供給で2.5Vくらいに下げて,温度検出分 解能を現状の2倍くらいに上げて1℃単位での制御安定度を向上させる方法もありそうです.
プログラムに関しては,パワーオン時にUP/DWN SWを同時に押していれば,フィードバック制御ではなく,一定値制御モードに入るような機能も考えられます.たとえば、デフォルト70%で起動し, UP/DWN SWで2%づつ出力値を変更可能とし,設定温度は表示しないで,その定電力値で動作している現在温度を表示するモードです.
この作品では,LCD表示器とSWがユーザ・インターフェースを行い,入力がA-Dコンバータで出力がD-Aコンバータ(PWM)のディジタル・ サーボの基本的なハードウェア構成となり,また,ソフトウエアはC言語の開発環境が無償でMICROCHIP社から提供されていますので,PICとC言語 で何か工作してみようと思われる方は,参考にしてぜひ面白いオリジナル作品にチャレンジしてみてください.
中川 裕三
