はじめに
以前の出版物では、 STM32、C ++、およびFreeRTOS。 最初から開発。 パート1私
SR0:デバイスは、3つのパラメーターを測定する必要があります(3つの変数があります): マイクロプロセッサー温度 、 VDDA 電圧 、 可変抵抗器による電圧
SR1:デバイスは、これらの変数の値をインジケーターに表示する必要があります。
SR2: マイクロプロセッサーの温度の単位は摂氏で、その他のパラメーターはボルトです。
SR3: ボタン1を押すと、 ディスプレイに次の測定変数の画面が表示されます。
SR4: ボタン1が押されると、 LED 1は状態を変更する必要があります
SR5: ボタン2を押すと、インディケーターは変数の表示モードを変数の常時表示から順次に変更する必要があります(1.5秒ごとに画面を変更します)。
SR6: ボタン2を押すと、 LED 2の状態が変わるはずです。
SR7: LED 3は1秒ごとに1回点滅します。
開発:ADC
新しいマイクロコントローラーのすべての知恵を理解したと判断したので、3つの値を測定するためにSR0の最も野心的な要件(実際はこれがデバイスの主な機能です)を取ることにしました。
まず、ADCに対処する必要がありました。 特に、マイクロコントローラのドキュメントを読むことなく、特別なツールCrt-CとCtr-Vを装備して、このユニットをオンザフライで使用することに決めたので、LEDとボタン制御アーキテクチャのコピーを描きました。

しかし、一般的に非常に機能しているこの素晴らしい図面を実現し始めた彼は、ドキュメントを読むことに興味を持ち、DMAチャネルを使用してアクティブなオブジェクトなしでそれを行うことができることに気付きました。 もちろん、すべてのマイクロコントローラーにそのようなユニットがあるわけではないという単純な理由で、そのようなアーキテクチャーは既にプロセッサーに依存していますが、マイクロコントローラーの機能を使用して物事を少し簡素化する方法を示すことは興味深いと思います。
そして、このようにそれをやり直しました:

すべてのアーキテクチャの準備ができており、ここで私は凍結しています。 ADCのセットアップはポートよりも少し複雑であることがわかりました。また、可変抵抗を使用して電圧を頑固に測定していません。 Vddaには温度がありますが、変数を使用する方法はありません。 繰り返しますが、同じリソースがADCの構成に役立ち、 STM32Lプロジェクトの作成に役立ちました。 ADC-A / DコンバーターおよびSTM32L。 DMAコントローラー そして、ボードOlimexのドキュメントとともにダウンロードされた可変デモプロジェクトに対処します。 彼は、別のレッグのPortD.Pin1プロセッサを接続するだけでよいことが判明しました。 いつものように、私はすべての鉄の設定を__low_level_init()に投げました
ADCおよびDMA設定
// () PORTD_PIN1 GPIOD->MODER |= GPIO_MODER_MODER1_0; GPIOD->PUPDR |= GPIO_PUPDR_PUPDR1_0; GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1; // , 12 , 16- , 17 - VDDA, // 22 - , // , , , // EOC, EOC , http://chipspace.ru/stm32l-discovery-adc/ // 16() 17(vdda) // 22() , 2- vdda, 3- ADC1->CR2 |= (ADC_CR2_DELS_2 | ADC_CR2_CONT); ADC1->CR1 |= ADC_CR1_SCAN; // GPIOE.7 - GPIOE->MODER |= GPIO_MODER_MODER7; //3 ADC1->SQR1 |= ADC_SQR1_L_1; // ADC_IN 16 1 , 305 // ADC_IN 17 2 , 305 // ADC_IN 22 3 , 305 ADC1->SQR5 |= ADC_SQR5_SQ1_4 | ADC_SQR5_SQ2_0 | ADC_SQR5_SQ2_4 | ADC_SQR5_SQ3_1 | ADC_SQR5_SQ3_2 | ADC_SQR5_SQ3_4; // 16 17 22 301 279 ADC1->SMPR2 |= ADC_SMPR2_SMP16 | ADC_SMPR2_SMP17_2; ADC1->SMPR1 |= ADC_SMPR1_SMP22_2; // VDDA ADC->CCR |= ADC_CCR_TSVREFE; // DMA ADC1->CR2 |= (ADC_CR2_DMA | ADC_CR2_DDS); // DMA // - , . DMA1_Channel1->CCR &= ~DMA_CCR1_DIR; // . DMA1_Channel1->CCR &= ~DMA_CCR1_PINC; // . DMA1_Channel1->CCR |= DMA_CCR1_MINC; // - 16 . DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; // - 16 DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0; // - (Very High) DMA1_Channel1->CCR |= DMA_CCR1_PL; DMA1_Channel1->CCR |= DMA_CCR1_CIRC;
クラス実装ファイル自体:
adc.h
#include "types.h" // #define SENSORTEMPERATURE_CHANNEL 0 #define VDDA_CHANNEL 1 #define TRIMMER_CHANNEL 2 class cAdc { public: explicit cAdc(const tU32 memoryBaseAddr, const tU8 measureCount); tBoolean switchOn(void); tBoolean startConversion(void); tBoolean isConversionReady(void); tF32 getValue(void) const; private: void initDma(const tU32 memoryBaseAddr, const tU8 measureCount); };
adc.cpp
#include <stm32l1xx.h> // STM2 #include "adc.h" // #include "susuassert.h" //for ASSERT #include "bitutil.h" // SETBIT, CLRBIT #define ADC1_DR_ADDRESS ((tU32)0x40012458) /******************************************************************************* * Function: constructor * Description: DMA RAM, * ******************************************************************************/ cAdc::cAdc(const tU32 memoryBaseAddr, const tU8 measureCount) { ASSERT(measureCount != 0); this->initDma(memoryBaseAddr, measureCount); } /******************************************************************************* * Function: switchOn * Description: ******************************************************************************/ tBoolean cAdc::switchOn(void) { tBoolean result = FALSE; // , 299 CD00240194.pdf SETBIT(ADC1->CR2, ADC_CR2_ADON); result = tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_ADONS)); return result; } /******************************************************************************* * Function: startConversion() * Description: ******************************************************************************/ tBoolean cAdc::startConversion(void) { tBoolean result = FALSE; // , 299 CD00240194.pdf SETBIT(ADC1->CR2, ADC_CR2_SWSTART); result = tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_STRT)); return result; } /******************************************************************************* * Function: getValue() * Description: ******************************************************************************/ tF32 cAdc::getValue(void) const { tF32 result = ADC1->DR; return result; } /******************************************************************************* * Function: isConversionReady() * Description: ? ******************************************************************************/ tBoolean cAdc::isConversionReady(void) { tBoolean result = tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_EOC)); return result; } /******************************************************************************* * Function: initDma() * Description: DMA ******************************************************************************/ void cAdc::initDma(const tU32 memoryBaseAddr, const tU8 measureCount) { // - . DMA1_Channel1->CPAR = ADC1_DR_ADDRESS; // - RAM. DMA1_Channel1->CMAR = memoryBaseAddr; DMA1_Channel1->CNDTR = measureCount; // DMA SETBIT(DMA1_Channel1->CCR, DMA_CCR1_EN); }
adcdirector.h
#include "adc.h" // cAdc #define MEASUR_NUMBER (tU8) 3 class cAdcDirector { public: explicit cAdcDirector(void); void startConversion(void); __IO uint16_t channelValue[MEASUR_NUMBER]; // private: cAdc *pAdc; };
adcdirector.cpp
#include "adcdirector.h" // /******************************************************************************* * Function: constructor * Description: , RAM, * DMA . ******************************************************************************/ cAdcDirector::cAdcDirector(void) { this->pAdc = new cAdc((tU32)&channelValue[0], MEASUR_NUMBER); this->pAdc->switchOn(); } /******************************************************************************* * Function: startConversion * Description: , DMA * channelValue ******************************************************************************/ void cAdcDirector::startConversion(void) { this->pAdc->startConversion(); }
インジケーターへの出力がまだないため、デバッガーの下でのみ作業を確認することができました。 ただし、その前に、main()にクラスの新しいインスタンスの作成を追加する必要があります
メイン()
void main( void ) { // ButtonControllera // , . //, static tTaskHandle tasksToNotifyFromButton[BUTTON_TASKS_NOTYFIED_NUM]; cAdcDirector *pAdcDirector = new cAdcDirector(); pAdcDirector->startConversion(); cLedsDirector *pLedsDirector = new cLedsDirector(); oRTOS.taskCreate(pLedsDirector, LEDSDIRECTOR_STACK_SIZE, LEDSDIRECTOR_PRIORITY, "Leds"); tasksToNotifyFromButton[LEDS_TASK_HANDLE_INDEX] = pLedsDirector->taskHandle; cButtonsController *pButtonsController = new cButtonsController(tasksToNotifyFromButton, BUTTON_TASKS_NOTYFIED_NUM); oRTOS.taskCreate(pButtonsController, BUTTONSCONTROLLER_STACK_SIZE, BUTTONSCONTROLLER_PRIORITY, "Buttons"); oRTOS.startScheduler(); }
デバッグ用に開始し、次の図を取得しました。channelValue[]配列の3つの値がすべて変更され、赤で強調表示されていることがわかります。 私は値をチェックしませんでしたが、一目で-ほぼ同様のもの。

いつものように、プロジェクトはここに保存されました: IAR 6.50のADC、ボタン、LED
開発:変数
そして、ADCは機能しているように見え、これらのユニットとゼロの山を人々が理解できるものに変える時が来ましたが、温度と電圧があります:
最初に、すべての変数に対して単一のインターフェースを考えました。 ここでは、仮想メソッドは1つしかありません。独自の計算と、計算された値を取得する1つの方法です。

そして、温度がどのようになるかを描きました。

そしてその後、測定結果を平滑化するフィルターがあればいいと思いました。 また、すべての変数にはフィルターが必要なので、インターフェースの形で作成できます。 その結果、次の概念が作成されました。

温度クラスは、インターフェース計算メソッドを実装します。 しかし、ここでは、マイクロコントローラーに配線された工場係数を使用して温度を計算するために、コメントを作成する必要があります。理論的には、このコードを別のプラットフォームに移植するには、不揮発性パラメーターにアクセスするクラスを作成し、これらの係数を必要とするクラスにこのクラスへのリンクを渡す必要があります、この場合、温度。 しかし、私は本当に3つの要因のために時間を使い果たしたくなかったし、町をやりたくなかったので、時間の不足のためにそのような枠を消し去り、港がここでは動作しないことを心に刻みます(大丈夫:))。 この全体の実装は次のようになります。
ivariable.h
#include "types.h" // #include "adcdirector.h" // cAdcdirector class iVariable { public: explicit iVariable(const cAdcDirector *pAdcDirector); virtual tF32 calculate(void) = 0; tF32 getValue(void) const {return value;}; protected: const cAdcDirector *pAdcDirector; tF32 value; };
ivariable.cpp
#include "ivariable.h" // #include "susuassert.h" // for ASSERT /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ iVariable::iVariable(const cAdcDirector *pAdcDirector) { ASSERT(pAdcDirector != NULL); this->pAdcDirector = pAdcDirector; this->value = 0.0F; }
temperature.h
#include "types.h" // #include "adcdirector.h" // cAdcdirector #include "ifilter.h" // iFilter #include "iVariable.h" // iVariable class cTemperature : public iVariable, private iFilter { public: explicit cTemperature(cAdcDirector *pAdcDirector); tF32 calculate(void); };
temperature.cpp
#include "temperature.h" // // 110 - 30 ( ), 289 #define DELTA_110_30 80.0F // , 28 , 30 :) #define DEGREE_30 28.0F // 2 102 CD00277537.pdf #define TS_CAL2_ADDR 0x1FF8007C // 1 102 CD00277537.pdf #define TS_CAL1_ADDR 0x1FF8007A // VDDA 3.0 #define VDDA_CAL_ADDR 0x1FF80076 #define FILTER_CONST 20.0F /******************************************************************************* * Function: constructor * Description: ******************************************************************************/ cTemperature::cTemperature(cAdcDirector *pAdcDirector) : iVariable(pAdcDirector) { } /******************************************************************************* * Function: calculate * Description: ******************************************************************************/ tF32 cTemperature::calculate(void) { tF32 temperature = 0.0F; // tF32 vdda = 0.0F; // vdda // , 289 CD00240193.pdf // 102 CD00277537.pdf tF32 tsCal2 = (tF32)(*((tU32 *)(TS_CAL2_ADDR)) >> 16); tF32 tsCal1 = (tF32) (*((tU32 *)(TS_CAL1_ADDR ))); tF32 vddaCal = (tF32)(*((tU32 *)(VDDA_CAL_ADDR)) >> 16); temperature = (tF32)this->pAdcDirector->channelValue[SENSORTEMPERATURE_CHANNEL]; vdda = (tF32)this->pAdcDirector->channelValue[VDDA_CHANNEL]; // 3.0 VDDA, // vdda, // 289 CD00240193.pdf temperature = DELTA_110_30 * ((temperature * vddaCal)/vdda - tsCal1) / (tsCal2 - tsCal1) + DEGREE_30; this->value = this->filter(this->value, temperature, FILTER_CONST); return this->value; }
変数VddaおよびTrimmer(可変抵抗器)でも同じフォーカスが繰り返されます。 アーキテクチャは、cTemeperatureクラスのアーキテクチャと同じです。
まあ、それがプロジェクトの主な要件であり、計画よりも長くかかったことを認めなければなりません-ほぼ7日間。 しかし、これは可変抵抗器の電圧に関する誤解によるものです。非常に長い間、何も測定されないのは愚かです。 私はOlimexボードの開発者をスパイすることをよく考えました:) 1つの小さなタスクが残っていました-インディケータへの出力。 8年前の最後のプロジェクトは、インジケータードライバーが組み込まれたPIC16のみで、インジケーターは非常にシンプルなので、複雑なものはないと思いました。 そして、それがどのように起こったのか、最後の部分で説明します。