« 第3回 dsPICマイコン基板を使った「セラミック・ヒータはんだゴテ用温度コントローラ」の製作 | メイン | dsPIC30F2012によるセラミック・ヒータはんだゴテ温度コントローラの改造 »

第4回 dsPICマイコン基板を使った「セラミック・ヒータはんだゴテ用温度コントローラ」の製作


 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 Request

 unsigned int c_mode;  //Constant Power Mode
 
// Moving Average Work Buffer

 unsigned 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 handler

void __attribute__ ((__interrupt__)) _T1Interrupt(void)
{
  IFS0bits.T1IF =0;             // clear interrupt
  flagT1 =1;     // Set T1_Done flag
}

//----------------------------------------------------------------------

//----------------------------------------------------------------------
// TIMER2 interrupt handler

void __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.3Hz

 T1CON=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 6

 T2CON = 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 stabilized

  ADCON1bits.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_tmp

  if (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 IronControl_10.c

 最後の部分の1秒間を数えているルーチンで,トラ技基板のLEDを赤緑反転させて,ハングしていないか一目でわかるようにしています.デバッグでLCDが動き出すまで,これは大いに役立ちました.

 筆者が使用した参考資料は,dsPICを操作する部分に関しては,トラ技とMICROCHIP社の資料など,またC言語の内部変数の処理や変数定義などの一般論に関しては,1980年代に発刊された「プログラミング言語C」(カーニハン,リッチー著)を使用しました.

●実装
 ハード ウェアは手持ちのジャンク部品などで高圧パワー部とPIC/LCD部に分けて組み,端材で適当に作り上げた木製ケースに組み込みました.とくにシールドも 施していませんので、EMI的には盛大にノイズを放出していてAMラジオとは同居ができないと思われます.実機の写真を参照してください.


IMG_0213.JPG

写真1 木製ケースに実装したようす

 また,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言語 で何か工作してみようと思われる方は,参考にしてぜひ面白いオリジナル作品にチャレンジしてみてください.

中川 裕三

連載 第1回 第2回 第3回 第4回 改造

カテゴリ:

トラックバック

このエントリーのトラックバックURL:
http://www.eleki-jack.com/mt/mt-tb.cgi/2308

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)

カテゴリ

会社案内
情報セキュリティおよび個人情報の取り扱いについて

コメントとトラックバックは、spamを予防するために、編集担当が公開の作業をするまで非公開になっています。
コメントはそれぞれ投稿した人のものです。

Powered by
Movable Type 4.1