こんにちは、しゅうです。
今回はセンサーの値を見るためにAD変換を試していきます。
今回は以下のように進めます。
はじめに、センサーとNucleoボードをつなげます。
次にどういうコードを実装するか考えて、実装します。
そして実行しますが、一部問題が起きたので、原因の解析と修正を行います。
再度確認してからまとめに入ります!
回路
今回使用する光センサはこちらのフォトトランジスタです。こちらから購入することができます!
データシートを見ると、出っ張りがある方がEmitterで、もう片方がCollectorです。電源はCollectorに繋げ、GNDをEmitterに繋げましょう。データシートにある回路例を見ると、EmitterとGNDの間に抵抗を挟んでいます。この抵抗値の大きさによってセンサの応答時間が変わるそうですが、今回は適当な抵抗を使います。
センサ値はEmitter側から拾うことができます。
これらをまとめて、以下の画像のような回路になりました。赤は電源(3.3V)、緑はマイコン側のAD変換を読み取るピン(後述)に接続します。
そして、この回路を以下の画像のようにNucleoボードに接続します。赤は3V3と書かれているところ、青はGNDと書かれているところと接続しましょう。そして、AD変換を読み取るピンはNucleoボード上ではA0書かれているところに接続します。
どういうコードにする?
確認したいこと
今回の実装で確認したいことは以下の3つが挙げられます。
- 回路設計が正しい
- AD変換を行い、値の取得ができている
- 取得した値を正しく表示できている
これらができれば、Pi:Coで行っていた時のように、センサ値を取得してそれに合う処理を実行したり、表示させて許容値を設定するパラメータ調整を行なったりができるようになります。
確認手法
具体的な確認手法を考えてみます。
まず1番については、とりあえず何かしらの値を取得したらLEDをつけたり消したりさせて電気的に正しく接続されているか確認します。
2番については、上記ができていれば半分解決しそうです。
3番については、取得した値をUART通信を使って表示させられれば、2番と同時に確認できてバッチリです。
以上を踏まえてコーディングに進みます!
STM32CubeIDEで設定
今回は、新規プロジェクトを作る時に、マイコン単体ではなく、NUCLEO-F103RBを選択して作ります。
既に色々設定されていますが、一箇所追加のピン設定を行います。
Analog欄のADC1を選択しましょう。すると、AD変換時に使える入力番号の一覧が出てきます。ハイライトされている番号は、他のピン割り当てと重なってしまい設定できない状態を示しています。今回はひとまずIN0を選択します。すると、PA0ピンがADC1_IN0に割り当てられます。
残りのピン配置はそのままで大丈夫です。念のため、確認ように以下の参考画像を添えます。
この状態で、コードを出力し、次の章に進みます。
追加コード
以下が今回追加したコードです。while文ではまずADCに関係する関数を呼び出します。HAL_ADC_Start関数でAD変換を開始し、HAL_ADC_PollForConversion関数で変換が完了するまで待ちます。この関数の第2引数はタイムアウトする時間(ms)を指定しています。
完了したら、adc_val変数にHAL_ADC_GetValue関数を使って、センサの値を代入します。ここまでの一連を、以下のコードでハイライトしています。
その値が1より大きければ、LEDをon/offし、受信した旨とセンサ値を送信させます。1以下の場合は、LEDに対しては何もせず、別途設定した文字列を送信します。
どちらもif文を抜ける前に0.5秒待たせています。
/* USER CODE BEGIN 2 */ uint32_t adc_val; char msg[30] = "Received data : "; char low_msg[] = "Lower than constant number \n\r"; char init[] = "Started! \n\r"; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_UART_Transmit(&huart2, (uint8_t *)init, sizeof(init), 100); while (1) { // ADC sequence HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 1000); adc_val = HAL_ADC_GetValue(&hadc1); if(adc_val > 1){ // Outputting the sensor value via UART, if value is more than 1 // Toggle the LED between on and off HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // Transmit word string HAL_UART_Transmit(&huart2, (uint8_t *)msg, sizeof(msg), 100); HAL_Delay(500); }else{ // Output low word string if value is lower or equal to 1 // Transmit word string HAL_UART_Transmit(&huart2, (uint8_t *)low_msg, sizeof(low_msg), 100); HAL_Delay(500); } /* USER CODE END WHILE */
実行①
では、先ほどのコードをビルドして、Nucleoをパソコンと接続して、書き込みます。UART通信に関しては前回の記事を参照してください。
光センサを覆ったり覆わなかったりすると、それに応じてLEDが切り替わったり切り替わらなかったりします。なので、確認したいことの1番と2番の途中まではクリアです!
でも思ってたのと違う結果が表示されました。
よく見ると、普段見かけない文字がありますね。
問題の原因調査と分析
まず問題が2つ混在しておりました。2つ目の問題に関しては、1つ目の問題を解決しようとmsg変数に、adc_val変数の値をまとめるように書いていたときに気づきました!
- adc_val変数をそのまま送信してもうまく拾ってくれない
- msg変数を初期化していない
原因調査自体は、1行ずつ動きをみて考えました。本当なら、デバッガーがついているので、STM32CubeIDEのデバッグ機能を使えば変数の中身を見ることができますが、それはまた別の機会で!
修正コード
というわけで、先のコードに修正を加えていきます。
- HAL_UART_Transmit関数にまとめて文字列を送る
- msg変数を初期化する
1番はsprintf関数を利用して、msg変数にadc_val変数の値を格納しています。2番に関しては、while文の直後にmemset関数を使ってヌル文字で初期化しています。(ついでにadc_val変数も毎回初期化するようにしました)
/* USER CODE BEGIN 2 */ uint32_t adc_val; char msg[30]; char low_msg[] = "Lower than constant number \n\r"; char init[] = "Started! \n\r"; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_UART_Transmit(&huart2, (uint8_t *)init, sizeof(init), 100); while (1) { // initiate variables adc_val = 0.0; memset(msg, '\0', sizeof(msg)); // ADC sequence HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 1000); adc_val = HAL_ADC_GetValue(&hadc1); // Adding sensor value to msg variable sprintf(msg, "Received data: %d \n\r", adc_val); if(adc_val > 1){ // Outputting the sensor value via UART, if value is more than 1 // Toggle the LED between on and off HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // Transmit word string HAL_UART_Transmit(&huart2, (uint8_t *)msg, sizeof(msg), 100); // Transmit sensor value HAL_UART_Transmit(&huart2, (uint8_t *)adc_val, sizeof(adc_val), 100); HAL_Delay(500); }else{ // Output low word string if value is lower or equal to 1 // Transmit word string HAL_UART_Transmit(&huart2, (uint8_t *)low_msg, sizeof(low_msg), 100); HAL_Delay(500); } /* USER CODE END WHILE */
実行②
改めてビルドしてかきこみます。実行するのドキドキしますね!
その結果…無事値の取得ができました〜!よかったよかった。数値が大きくなると(4桁など)用意してたmsg変数の大きさが小さいみたいで表示がたまにバグりますけど、今後修正していきます!
まとめ
以上、無事AD変換を実装することができました!ここからは、複数のAD変換を行ったり、割り込みで行ったりなど、まだまだやることは山積みです。
モータを回転するための出力などをする必要もありますね。
引き続き頑張っていきます!次回もお楽しみに〜