PSoCとLM35で温度を計測する

psoc_adc_01.gif
今回はPSoCに温度計IC「LM35」とキャラクタ液晶モジュールを利用した電子温度計を作る。今回はPSoC Designer(C言語)を利用した。

LM35は10mV/℃の電圧として温度が出力される。つまり20℃であれば200mV(20℃×10mV/℃)、25℃であれば250mVとなる。そのため電圧計に接続すればそのまま温度を読み取れるICだ。
このLM35の出力電圧をPSoCのA/Dコンバーターで数値に変換し、LCDモジュールで数値として表示する。

今回利用した温度センサーの「LM35」は秋月電子で150円程度、PSoCの「CY29466」は500円程度で販売されている。

psoc_adc_02.gif
プロジェクトの作成時に「C」を選択する。

今回作成するプロジェクトはサンプルコードを切り貼りしたもの+α程度のためアセンブラでも比較的簡単に実現できる。とは言うもののアセンブラではソースコードが煩雑でバグも入り込みやすい。そのためC言語を利用した。PSoC Designer自体は無料ソフトだがC言語を利用する場合は、別途「Cライセンス」の購入が必要だ。秋月電子などで1万6000円もする高価なものだが、開発効率が確実によくなるので買っておいて損はないだろう。

psoc_adc_03.gif
デバイスエディターではプロジェクトで利用するモジュールを選択する。

A/Dコンバーターは12ビットの「ADCINC12」を利用する。「ADCINC12」の上で右クリックをして現れたメニューから「Select」を選択する。

psoc_adc_04.gif
LM35の出力電圧は(室温では)1V未満の低い電圧となる。0~5Vの範囲で入力できるA/Dコンバーターの能力をフルに生かしきれずもったいない。そのためLM35の出力電圧を増幅するためのアンプを使うことにした。今回は「Amplifiers」の「PGA」を利用する。「ADCINC12」のときと同様に右クリックをして「Select」を選択する。

psoc_adc_05.gif
計測値を表示する液晶用のモジュールは「Misc Digital」の「LCD」になる。これも右クリックして現れたメニューから「Select」を選択する。

psoc_adc_06.gif
「ADCINC12」、「PGA」、「LCD」の各モジュールを選択し終えたら「Config」メニューの「Interconnect」を選択して画面を切り替える。

psoc_adc_07.gif
配線などの設定画面に切り替わった。ここではまず画面左側上部の「GlobalResources」を設定する。
「VC1」を「16」に、
「VC2」を「8」に、
「Ref Mux」を「(Vdd/2)+/-(Vdd/2)」にする。特に3つ目の「Ref Mux」の設定は重要だ。これを怠るとA/Dコンバーターからの数値が0やFFFとなってしまいきちんと読み取れないことがある。
さらに「LCD_1」の接続用ポート「LCDPort」を「Port_2」に設定する。

psoc_adc_08.gif
次に「ADCINC12_1」を右クリックして現れたメニューから「Place」を選択してA/Dコンバーターを配置する。

psoc_adc_09.gif
そして左側から設定をする
「TMR Clock」を「VC2」に、
「CNT Clock」を「VC2」に、
「Input」を「ACB00」にする。ちなみにポートからアンプを介さずに直接A/Dコンバーターへ入力したい場合は「Input」の部分でポートを指定すればいい。

psoc_adc_10.gif
次にアンプを設定する。「PGA_1」を右クリックして現れたメニューから「Place」を選択して配置する。

psoc_adc_11.gif
左側から設定をする。
「Gain」を「1.000」に、
「Input」を「AnalogColumn_InputMUX_0」に、
「Reference」を「VSS」に、
「AnalogBus」を「Disable」にする。PGAで重要なのは増幅倍率を示す「Gain」だ。ここではとりあえず"1倍"、つまり増幅なしに設定している。後々の作業で倍率を変更する。

psoc_adc_12.gif
次に内部配線を行う。作業しやすいように内部配線図の画面を拡大し、PGAの隣りの部分をクリックする。

psoc_adc_13.gif
するとメニューが現れるので「VC2」を選択する。

psoc_adc_14.gif
次に「PGA」の上の部分をクリックする。

psoc_adc_15.gif
ここでは「Port_0_1」を選択する。これでポート01からアンプ「PGA」を介してA/Dコンバーター「ADCINC12」に入力される。

psoc_adc_16.gif
一通り設定が終わったのでソースコードを書くため画面を切り替える。「View」メニューから「Application Editor」を選択する。

psoc_adc_17.gif
PSoC Designerではサンプルコードを使うと開発しやすい。まずはA/Dコンバーターのサンプルコードを開く。ツールバーで「ADCs」の「ADCINC12」を選択して「User Module Datasheet」ボタンを押す。

psoc_adc_18.gif
そしてデータシートの中に記述されているサンプルソースコードのメイン処理部分をコピーする。

psoc_adc_19.gif
そして「main.c」のメイン処理の中に貼り付ける。このとき「ADCINC12_」となっている部分を全て「ADCINC12_1_」に置き換える。
次にアンプのサンプルソースコードを開く。「Amplifiers」から「PGA」を選択し「User Module Datasheet」ボタンを押す。

psoc_adc_20.gif
「PGA」のサンプルコード処理では初期化処理部分のみをコピーする。

psoc_adc_21.gif
そしてメイン処理の先頭部分に貼り付ける。
次に液晶モジュールのサンプルコードを開くため「Misc Digital」の「LCD」を選択して「User Module Datasheet」ボタンを押す。

psoc_adc_22.gif
今回は初期化処理と表示位置設定部分をコピーする。

psoc_adc_23.gif
さらに一覧されているAPIの部分から2バイト数値を表示するための関数「LCD_PrHexInt」をコピーする。

psoc_adc_24.gif
そして初期化処理をメイン処理の先頭に、表示位置指定処理と数値表示処理をA/Dコンバーターの数値読み取り処理の後に貼り付ける。
これまでの作業でソースコードは以下のようになる。

//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules


INT iData;
void main()
{
LCD_1_Start(); // Initialize LCD

PGA_1_Start(PGA_1_MEDPOWER);

M8C_EnableGInt; // Enable Global Interrupts
ADCINC12_1_Start(ADCINC12_1_HIGHPOWER); // Apply power to the SC Block
ADCINC12_1_GetSamples(0); // Have ADC run continuously
for(;;){
while(ADCINC12_1_fIsDataAvailable() == 0); // Loop until value ready
ADCINC12_1_ClearFlag(); // Clear ADC flag
iData=ADCINC12_1_iGetData(); // Get ADC result
// Add user code here to use or display result

LCD_1_Position(0,5); // Place LCD cursor at row 0, col 5.

LCD_1_PrHexInt(iData);
}
}





_DSC8993.JPG
動作確認にはCY8C29466とPSoCEvalボードを利用した。液晶モジュールはポート2に、LM35はポート01に接続する。

_DSC8994.JPG
LM35は写真で見て左側の端子からVCC、OUT、GNDとなる。0.01μF程度のコンデンサをVCCとGND間、OUTとGND間に用意して、OUTをポート01に接続すればいい。

_DSC8995.JPG
表示された値は「F8D8」となる。実際の温度に直すにはまず表示値(0xF8D8)に0x0800を加算する。その結果値(0x00D8=216)は0~5000mVを0~4096で表現したときの値になるため、結果値(216)×5000÷4096≒263.67mVとなる。これは26.367℃に相当する。

要は「(測定値+0x0800)×5000÷4096」を計算すればmV単にの計測電圧となる。


psoc_adc_25.gif
「F800」~「7FFF」という生の数値表示では表示されている数値から実際の温度への変換が煩雑だ。次に温度の16進数表示になるようにする。

まず計測値に2048を加算することで「0000」~「0FFF」という数値に変換する。そして5000mV/4096に相当する1.22を乗算した。

//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules


INT iData;
void main()
{
LCD_1_Start(); // Initialize LCD

PGA_1_Start(PGA_1_MEDPOWER);

M8C_EnableGInt; // Enable Global Interrupts
ADCINC12_1_Start(ADCINC12_1_HIGHPOWER); // Apply power to the SC Block
ADCINC12_1_GetSamples(0); // Have ADC run continuously
for(;;){
while(ADCINC12_1_fIsDataAvailable() == 0); // Loop until value ready
ADCINC12_1_ClearFlag(); // Clear ADC flag
iData=ADCINC12_1_iGetData(); // Get ADC result
// Add user code here to use or display result

LCD_1_Position(0,5); // Place LCD cursor at row 0, col 5.

iData += 2048;
iData *= 1.22;
LCD_1_PrHexInt(iData);
}
}





_DSC8996.JPG
これで表示値がそのまま実際の入力電圧となる。この場合は0x010C=268mV(26.8℃)となる。

psoc_adc_26.gif
16進数表示のままだと面倒なので次に普通の10進数で表示されるようにする。

ここでは簡易的なatoi関数を用意した。これでソースコードは以下のようになる。同様の処理をアセンブラで実現したい場合は「MiscDigital」の「LED7SEG」の中の処理が参考になるだろう。


//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules

char* itoa(int nData,char* pszData)
{
int pn2n[] = {10000,1000,100,10,1};
int i;
char* pszTmp;

pszTmp = pszData;
for(i = 0; i < 5; i++)
{
(*pszTmp) = 0;
while(nData >= pn2n[i])
{
nData -= pn2n[i];
(*pszTmp)++;
}
(*pszTmp) += '0';
pszTmp++;
}
pszData[5] = 0;
}

INT iData;
void main()
{
LCD_1_Start(); // Initialize LCD

PGA_1_Start(PGA_1_MEDPOWER);

M8C_EnableGInt; // Enable Global Interrupts
ADCINC12_1_Start(ADCINC12_1_HIGHPOWER); // Apply power to the SC Block
ADCINC12_1_GetSamples(0); // Have ADC run continuously
for(;;){
while(ADCINC12_1_fIsDataAvailable() == 0); // Loop until value ready
ADCINC12_1_ClearFlag(); // Clear ADC flag
iData=ADCINC12_1_iGetData(); // Get ADC result
// Add user code here to use or display result

LCD_1_Position(0,5); // Place LCD cursor at row 0, col 5.

iData += 2048;
iData *= 1.22;
{
char pszData[6];

itoa(iData,pszData);
LCD_1_PrString(pszData);
}
}
}





_DSC8997.JPG
ここまでくるとそのまま見た値が温度になる。この場合は27.2℃(272mV)だ。

psoc_adc_27.gif
最後にA/Dコンバーターの能力をフルに使うためにアンプの倍率を設定する。A/Dコンバーターは0~5V入力、対して温度計「LM35」は100℃でも1V出力と出力電圧が低い。仮りに温度計を0~50℃(0~500mV出力)まで使いたいとすると...5000mV÷500mV=10、つまりアンプの増幅率を10倍に設定すればいい。
今回用いた「PGA」の増幅率設定は8倍の次が16倍となっている。16倍では増幅しすぎなので8倍でアンプを利用すれば効率よくA/Dコンバーターを扱える。アンプで8倍するとA/Dコンバーターからの出力値も8倍されるので、表示値を見やすくするには8分の1にする必要がある。しかし単純に8で割ったのでは、増幅した意味がなくなる。そのため「計測値×(10÷8)」とする。つまり表示値を従来の10倍にするわけだ。実際のプログラムでは10÷8=1.25として、1.25を乗算する。

//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules

char* itoa(int nData,char* pszData)
{
int pn2n[] = {10000,1000,100,10,1};
int i;
char* pszTmp;

pszTmp = pszData;
for(i = 0; i < 5; i++)
{
(*pszTmp) = 0;
while(nData >= pn2n[i])
{
nData -= pn2n[i];
(*pszTmp)++;
}
(*pszTmp) += '0';
pszTmp++;
}
pszData[5] = 0;
}

INT iData;
void main()
{
LCD_1_Start(); // Initialize LCD

PGA_1_Start(PGA_1_MEDPOWER);
PGA_1_SetGain(PGA_1_G8_00);

M8C_EnableGInt; // Enable Global Interrupts
ADCINC12_1_Start(ADCINC12_1_HIGHPOWER); // Apply power to the SC Block
ADCINC12_1_GetSamples(0); // Have ADC run continuously
for(;;){
while(ADCINC12_1_fIsDataAvailable() == 0); // Loop until value ready
ADCINC12_1_ClearFlag(); // Clear ADC flag
iData=ADCINC12_1_iGetData(); // Get ADC result
// Add user code here to use or display result

LCD_1_Position(0,5); // Place LCD cursor at row 0, col 5.

iData += 2048;
iData *= (1.22 * 1.25);
{
char pszData[6];

itoa(iData,pszData);
LCD_1_PrString(pszData);
}
}
}





_DSC8998.JPG
これで計測値が1桁増え、表示は2690=269.0mV(26.90℃)となった。LM35は室温で±0.25℃程度の誤差があるので実際問題として表示精度を上げる意味はない。しかし一般的なA/Dコンバーターの使い方として役に立つ...かもしれない。


カテゴリー「電子工作」 のエントリー