2017年2月18日土曜日

温度を測ってみる


 TIMのADCでサーミスタの電圧を計測し、マイコン内で温度の計算までを行ってみた。室温が25℃ほど、温かいのは指でつまんだ時、冷えているのはティッシュをかぶせてパーツクリーナーを吹いた時。氷点下10℃になるとか潜熱凄まじい(温度センサが正しいのであれば。


 今回、STM32F103CBTを使っており、このマイコンにはADCが2本乗っているが、まずは超音波MICにつながっているADCを転用した。しかしこれがかなり面倒だった。
 超音波MICを正確なタイミングでADCを行うために、ADCの変換開始はTIMのトリガを使っている。が、温度の変換であればTIMでトリガをする必用はないので、ソフトウェアで変換開始を指示できれば手っ取り早い。しかし、StdPeriphLibにはADC_Init以外に外部トリガを変更する関数がみつからなかった。もちろん自前でレジスタを叩いて変更することもできるはずだが、今回は別の方法を選んだ。
 まず試したのはTIM_GenerateEventでTriggerイベントを発生させることだった。STM32F1のTIMにはソフトウェアからイベントを発生させられるので、コレでADCの変換ができれば非常に簡単なのだが、どうやらそのような動作はできないらしい。
 しょうがないので、別の方法でイベントを発生させる必要がある。ということで、次に試したのがTIM自体を回すという方法。しかし複数回の変換トリガがかかってしまうと具合がわるいので、1回だけTIMが動いて欲しい。ということで予めSelectOnePulseModeでSingleモードにし、その後CmdでTIMを開始、直後に停止。その後でRepetitiveモードに戻す、という動作を行った。もしかしたら直後にTIMを止めているのに不都合があるかもしれないが、とりあえず今のところは動いている。
 あとはADCの変換終了を待ち、レジスタを読んで計算を行う、という感じで、特記するところはない。


 なぜADCはGenerateEventで動かないかという話だが、ADCのトリガ入力はちょっと独特で、実際にエッジがペリフェラルから出力される必要がある。そのためTIMのOCなどでADCをトリガする際は、OutputStateにEnableを設定する必要がある(TIMのOCでDMAをトリガする際はOutputStateがDisableでも問題ない)。
 GenerateEventは、リファレンスマニュアルにはTGをセットした場合、「SR.TIFがセットされ、有効な場合は割り込みやDMA転送が発生する」というような事が書いてある(割り込みが発生するのはSR.TIFの影響?)。TG以外はというと、OCの場合も似たようなもので、割り込みフラグのセットやDMA転送のトリガしか書かれておらず、OC状態が変化するとは書かれていない。
 ADCはペリフェラル間を直結したルートではなく、GPIOに近いところでエッジを検出しているので、実際にペリフェラルからパルスが出力されないGenerateEventでは動作しないのではないか、と思う。

 STM32F1のペリフェラル連携はこういうちょっと残念なところがたまにある。
 そもそもTIMのタイミングで何かを行いたい場合、大抵はDMA転送で事足りる。例えば1秒に100回、UARTで1バイトを送信したいなら、TIMからDMAにトリガがかかり、DMAがUART->DRにデータを渡し、それによりUART転送が始まる。SPIも同様で、TIMから直接UARTやSPIにタイミングが使われることはないはず。DAC(デジタル・アナログコンバータ)もDMA転送を経由する。
 ということを考えると、ADCとTIMの接続は、STM32F1のペリフェラル間連携ではかなり特殊な事例といえるはず。
 

 今のところ、PA0-5を超音波MIC、PB0-5, PB10-15を超音波SPに割り当てており、PA9, 10はUSART1、11以降はSTBee Miniのデフォルト機能が接続されている。残りはPA7, 8とPB6-9で、ADCを接続できるのはPA7のみ。PB8, 9はCANに予約で、PB6, 7はI2C1に予約かな。とりあえず空きはアナログ入力が1、GPIOはアナログも含めれば3箇所となる。ここまでピンを最大限使うのもかなり久しぶりな気がする。



ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);

TIM_GenerateEvent(TIM3, TIM_EventSource_Update);
TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Single);
TIM_Cmd(TIM3, ENABLE);
TIM_Cmd(TIM3, DISABLE);
TIM_SelectOnePulseMode(TIM3, TIM_OPMode_Repetitive);

while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));

uint16_t value = ADC_GetConversionValue(ADC1);
const float Vin = 3.3f;
const float R2 = 18;
const float T0 = 25;
const float R0 = 10;
const float B = 3380;

float Vout = value * Vin / 4096;
float Rth = (Vin - Vout) / (Vout / R2);
float tempC = 1 / ((log(Rth / R0) / B) + 1 / (T0 + 273.15f)) - 273.15f;

printf("Rth:%f kOhm\n", Rth);
printf("tmp:%f degC\n", tempC);

0 件のコメント:

コメントを投稿