目次
1. 初めに
2. A/D設定
3. A/D処理
4. Generate Source、Build Project 注意点
5. ビルド・実行
6. 終わりに
1. 初めに
今回のテーマはA/D変換です。Exampleの EFM8BB3 ADC Lib Polled プロジェクトを教材としていろいろやって行きます。このプロジェクトのインストール方法はEFM8開発 - Simplicity Studioを使ってみる - Example編の EFM8BB3 Blinkey と同様で、プロジェクトの選択で EFM8BB3 ADC Lib Polled を選択します。

プロジェクトを作成し終えるとこの画面になります。

2. A/D設定
Hardware Configurator を用いてA/D変換に関わる設定を確認します。
EFM8B3_ADC_Lib_Polled.hwconf を右クリックし、 Open を選択します。

DefaultMode Port I/O をクリックしてポート設定画面を表示します。

| Property | Value | 備考 |
|---|---|---|
| IOMode | Analog I/O | アナログポートとして使用する |
| Skip | Skipped | クロスバー でのアサイン対象から除外する |
| Property | Value | 備考 |
|---|---|---|
| Enable Crossbar | Enabled | クロスバー 動作を許可する |
先のポートP1.7のプロパティ「クロスバー でのアサイン対象から外す」、Port I/Oのプロパティ「クロスバー 機能を許可する」とは、マイコンの機能としてのクロスバー動作は許可するけれど、ポートP1.7はそのアサイン対象から除外 ポートP.7はクロスバー 動作とは無関係、ということです。
ここで クロスバー という言葉が登場しましたが、これは、ペリフェラル入出力のピンアサインをユーザが設定できる機能です。これについては今後別の記事で説明する予定です。
尚、Enable Crossbar が Disabled 設定の場合、ポート出力動作が禁止となります。なので、クロスバー機能を使わない場合でも Enable Crossbar 設定は Enabled とします。
DefaultMode Peripherals をクリックします。ペリフェラル設定画面を表示します。更に ADC0 をクリックします。

主なものは
| Property | Value | 備考 |
|---|---|---|
| Enable ADC | Enabled | A/Dコンバータを有効にする |
| Start of Conversion | Write to 1 of ADBUSY | A/D変換開始モード:ADBUSYビットへ1を書き込むとA/D変換を開始する |
| Resolution | 12-Bit mode | A/D分解能:12bit |
| Positive Input Selection | ADC0.13(P1.7) | A/D入力チャンネル ADC0.13を設定する(Pin名:P1.7) |
| Select Voltage Reference | Internal VREF | A/D基準電圧:内部基準電圧 |
| Reference Voltage | 1.65 | A/D基準電圧が1.65Vであることを表している |
| Gain Control | 0.5x gain | ゲイン設定0.5倍モード |
| Full Scale Voltage | 3.300 | フルスケール入力電圧が3.3Vであることを表している (1.65 / 0.5) |
| Select ADC Clock | SYSCLK | ADCクロックはシステムクロックを使用する |
| Property | Value | 備考 |
|---|---|---|
| SYSCLK | 24.500 MHz | システムクロックが24.5MHzであることを表している |
| Select Clock Source | Internal High Frequency Oscillator 0 | システムクロックは内部オシレータHFOSC0を設定する |
| Clock Source Divider | SYSCLK / 1 | クロック分周設定は1:1 |
3. A/D処理
A/D処理はコードの中でどのようになっているのか main()から見て行きます。
void main (void)
{
uint16_t mV;
uint8_t joystickDir;
enter_DefaultMode_from_RESET();
DISP_EN = DISP_BC_DRIVEN; // Display not driven by EFM8
while (1)
{
//Start conversion
ADC0_startConversion();
// Wait for conversion to complete
while (!ADC0_isConversionComplete());
// Convert sample to mV
mV = ADC0_convertSampleToMillivolts(ADC0_getResult());
// Get joystick direction from ADC sample
joystickDir = JOYSTICK_convert_mv_to_direction(mV);
// Convert joystick direction to LED color
ConvertDirectionToColor(joystickDir);
}
}
enter_DefaultMode_from_RESET();
A/D変換、マイコンポート、その他初期設定を行います。
ADC0_startConversion();
A/D変換を開始します。
while (!ADC0_isConversionComplete());
A/D変換が終わるのを待ちます。
mV = ADC0_convertSampleToMillivolts(ADC0_getResult());
ADC0_getResult() でA/D値を取得し、その値を電圧値(単位mV)へ換算します。
ADC0_convertSampleToMillivolts() のソースコードです。
//-----------------------------------------------------------------------------
// ADC0_convertSampleToMillivolts
//-----------------------------------------------------------------------------
//
// Converts a raw ADC sample to millivolts.
// sample - the raw sample from the ADC
//
// returns - a two byte number representing the adc sample in millivolts
// e.g. 1.65V would equal 1650.
//
uint16_t ADC0_convertSampleToMillivolts(uint16_t sample)
{
// The measured voltage applied to P1.7 is given by:
//
// Vref (mV)
// measurement (mV) = --------------- * result (bits)
// (2^12)-1 (bits)
return ((uint32_t)sample * VREF_MV) / ADC_MAX_RESULT;
}
定数定義は
#define VREF_MV (3300UL)
#define ADC_MAX_RESULT ((1 << 12)-1) // 12 bit ADC
A/D値から電圧値への換算はコメント部分に記述してある通りですね。
VREF_MV は 3300、ADC_MAX_RESULT は 2^12-1 = 4095 を定義していますので、A/D値から電圧値を換算する計算式は
電圧値(mV) = \frac{3300}{4095}\times(A/D値)
となります。
ジョイスティック周辺の回路です(実際の回路図を一部省略しています)。

ジョイスティックは上下左右と中立状態で押下の5つのスイッチから成っています。スイッチON毎に異なる電圧値となるように各スイッチに抵抗器を接続しています。
| スイッチ | Vad(mV) | Vad 計算式 |
|---|---|---|
| Left | 1980 | 3300 * 15 / (10 + 15) |
| Down | 1650 | 3300 * 10 / (10 + 10) |
| Right | 2533 | 3300 * 33 / (10 + 33) |
| Up | 2831 | 3300 * 60.4 / (10 + 60.4) |
| Center | 33 | 3300 * 0.1 / (10 + 0.1) |
| Left + Down | 33 | 15kと10kの合成抵抗 (15*10)/(15+10)=6 3300 * 6 / (10 + 6) |
| Right + Down | 1433 | 33kと10kの合成抵抗 (33*10)/(33+10)=7.67 3300 * 7.67 / (10 + 7.67) |
| Left + Up | 1801 | 15kと60.4kの合成抵抗 (15*60.4)/(15+60.4)=12.01 3300 * 12.01 / (10 + 12.01) |
| Right + Up | 2247 | 33kと60.4kの合成抵抗 (33*60.4)/(33+60.4)=21.34 3300 * 21.34 / (10 + 21.34) |
joystickDir = JOYSTICK_convert_mv_to_direction(mV);
電圧値からジョイスティックの位置を計算します。
JOYSTICK_convert_mv_to_direction() のソースコードです。関連する箇所を抜粋します。
uint8_t JOYSTICK_convert_mv_to_direction(uint32_t mV)
{
uint8_t joystickDirection;
// determine which direction pad was pressed
if ((mV <= JOYSTICK_MV_C + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_C;
}
else if ((mV >= JOYSTICK_MV_N - JOYSTICK_MV_ERROR) && \
(mV <= JOYSTICK_MV_N + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_N;
}
else if ((mV >= JOYSTICK_MV_E - JOYSTICK_MV_ERROR) && \
(mV <= JOYSTICK_MV_E + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_E;
}
else if ((mV >= JOYSTICK_MV_S - JOYSTICK_MV_ERROR) && \
(mV <= JOYSTICK_MV_S + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_S;
}
else if ((mV >= JOYSTICK_MV_W - JOYSTICK_MV_ERROR) && \
(mV <= JOYSTICK_MV_W + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_W;
}
else
{
joystickDirection = JOYSTICK_NONE;
}
return joystickDirection;
}
定数定義は
#define JOYSTICK_NONE 0 ///< not pressed
#define JOYSTICK_C 1 ///< center
#define JOYSTICK_N 2 ///< north
#define JOYSTICK_E 3 ///< east
#define JOYSTICK_S 4 ///< south
#define JOYSTICK_W 5 ///< west
#define JOYSTICK_MV_C 3 ///< center position in mV
#define JOYSTICK_MV_N 2831 ///< north position in mV
#define JOYSTICK_MV_E 2533 ///< east position in mV
#define JOYSTICK_MV_S 1650 ///< south position in mV
#define JOYSTICK_MV_W 1980 ///< west position in mV
#define JOYSTICK_MV_ERROR 150 ///< tolerance in mV for cardinal
コード内では方向を
- north : Up
- west : Left
- south : Down
- east : Right
で表しています。各方向別に電圧上下範囲を設定しており、電圧値がその範囲に納まった場合方向が決まる仕組みです。例えば mV=2831 の場合、この分岐に至ることとなります。
else if ((mV >= JOYSTICK_MV_N - JOYSTICK_MV_ERROR) && \
(mV <= JOYSTICK_MV_N + JOYSTICK_MV_ERROR))
{
joystickDirection = JOYSTICK_N;
}
ConvertDirectionToColor(joystickDir);
ジョイスティックの位置に対応した色でLEDを点灯します。
ConvertDirectionToColor() のソースコードです。
//-----------------------------------------------------------------------------
// ConvertDirectionToColor
//-----------------------------------------------------------------------------
//
// Turns on LEDs depending on the joystick direction given
//
// dir - JOYSTICK_N = White
// JOYSTICK_E = Red
// JOYSTICK_S = Green
// JOYSTICK_W = Blue
// Any other direction = Off
//
void ConvertDirectionToColor(uint8_t dir)
{
GREEN_LED = LED_OFF;
BLUE_LED = LED_OFF;
RED_LED = LED_OFF;
switch (dir)
{
case JOYSTICK_N:
GREEN_LED = LED_ON;
BLUE_LED = LED_ON;
RED_LED = LED_ON;
break;
case JOYSTICK_E:
RED_LED = LED_ON;
break;
case JOYSTICK_S:
GREEN_LED = LED_ON;
break;
case JOYSTICK_W:
BLUE_LED = LED_ON;
break;
default:
break;
}
}
LED出力に関する定義です。
SI_SBIT(GREEN_LED, SFR_P1, 4); // Green LED
SI_SBIT(BLUE_LED, SFR_P1, 5); // Blue LED
SI_SBIT(RED_LED, SFR_P1, 6); // Red LED
#define LED_ON (0)
#define LED_OFF (1)
LED周辺の回路です。

Green、Blue、Redを同時ONでWhite(白)点灯となります。
4. Generate Source、Build Project 注意点
Generate Sourceを実行、その後ビルドすると・・・ 
Consoleのメッセージを見ると、 'ADC0CF1_ADLPM__LP_ENABLED'は未定義の識別子 と表示しています。
C:\Users\burni\SimplicityStudio\v5_workspace\EFM8BB3_ADC_Lib_Polled\src\InitDevice.c: 'ADC0CF1_ADLPM__LP_ENABLED': undefined identifier
そこで、エラーが発生している IntDevide.c を開いてエラー発生箇所を見ます。

ADC0CF1_ADLPM__LP_ENABLEDをプロジェクト内で定義してないのにこの行で使用しています。この行は Generate Source時に生成するので、どうやら Hardware Configurator 不具合の様な気がします(断言はしませんが)。
とりあえず対応策を示します。公式発表されているものではないです。
#define ADC0CF1_ADLPM__LP_ENABLED 0を InitDevice.c へ追加します。
//=========================================================
// src/InitDevice.c: generated by Hardware Configurator
//
// This file will be regenerated when saving a document.
// leave the sections inside the "$[...]" comment tags alone
// or they will be overwritten!
//=========================================================
// USER INCLUDES
#include <SI_EFM8BB3_Register_Enums.h>
#include "InitDevice.h"
// USER PROTOTYPES
// USER FUNCTIONS
+ #define ADC0CF1_ADLPM__LP_ENABLED 0 //定義を追加
// $[Library Includes]
// [Library Includes]$
尚、この現象は他のA/D変換を用いているExample ADC_Lib_Interrupt でも発生しました。

5. ビルド・実行
以前の記事 の
5. プロジェクトのビルド
6. 実行(デバッグ)
と同様です。
6. 終わりに
マイコン基板に合ったExampleが多数用意されており、他のハードウェアを用意せずともマイコン学習をすぐに始められるのは助かります。














