본문 바로가기
전자공학/LoRa 통신

EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(3)

by ohj921189 2020. 2. 26.
반응형

저번 포스팅에서는 [Software Example]에 있는 예제 리스트들을 찾아보고 그중 humitemp 예제를 Project Explorer에 추가해 보았습니다. 또한 humitemp 예제의 메인 코드를 불러오는 방법도 알아보고 humitemp 예제 코드의 앞부분을 분석해보았습니다. 이번 포스팅에서는 저번 시간에 이어 코드를 분석해보겠습니다. 아래는 코드의 전문입니다.

/***************************************************************************//**
 * @file
 * @brief Relative humidity and temperature sensor demo for SLSTK3400A_EFM32HG
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * The licensor of this software is Silicon Laboratories Inc. Your use of this
 * software is governed by the terms of Silicon Labs Master Software License
 * Agreement (MSLA) available at
 * www.silabs.com/about-us/legal/master-software-license-agreement. This
 * software is distributed to you in Source Code format and is governed by the
 * sections of the MSLA applicable to Source Code.
 *
 ******************************************************************************/

#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_gpio.h"
#include "i2cspm.h"
#include "si7013.h"
#include "sl_sleeptimer.h"
#include "graphics.h"
#include "em_adc.h"
#include "bspconfig.h"

/***************************************************************************//**
 * Local defines
 ******************************************************************************/

/** Time (in ms) between periodic updates of the measurements. */
#define MEASUREMENT_INTERVAL_MS      2000
/** Voltage defined to indicate dead battery. */
#define LOW_BATTERY_THRESHOLD   2800

/***************************************************************************//**
 * Local variables
 ******************************************************************************/
/* Variables used by the display callback. */
static void (*mem_lcd_callback_func)(void*) = 0;
static void *mem_lcd_callback_arg = 0;

/** Flag used to indicate ADC is finished */
static volatile bool adcConversionComplete = false;

/** This flag indicates that a new measurement shall be done. */
static volatile bool measurement_flag = true;

/** Timer used for periodic update of the measurements. */
sl_sleeptimer_timer_handle_t measurement_timer;

/** Timer used for periodic maintenance of the display **/
sl_sleeptimer_timer_handle_t display_timer;

/***************************************************************************//**
 * Local prototypes
 ******************************************************************************/
static void gpioSetup(void);
static uint32_t checkBattery(void);
static void adcInit(void);
static void measure_humidity_and_temperature(I2C_TypeDef *i2c, uint32_t *rhData, int32_t *tData, uint32_t *vBat);
static void measurement_callback(sl_sleeptimer_timer_handle_t *handle, void *data);

/***************************************************************************//**
 * @brief  Main function
 ******************************************************************************/
int main(void)
{
  I2CSPM_Init_TypeDef i2cInit = I2CSPM_INIT_DEFAULT;
  uint32_t         rhData;
  bool             si7013_status;
  int32_t          tempData;
  uint32_t         vBat = 3300;
  bool             lowBatPrevious = true;
  bool             lowBat = false;

  /* Chip errata */
  CHIP_Init();

  /* Use LFXO for rtc used by the sleeptimer */
  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
  CMU_ClockEnable(cmuClock_HFLE, true);

  /* Initalize peripherals and drivers */
  gpioSetup();
  adcInit();
  sl_sleeptimer_init();
  GRAPHICS_Init();
  I2CSPM_Init(&i2cInit);

  /* Get initial sensor status */
  si7013_status = Si7013_Detect(i2cInit.port, SI7021_ADDR, NULL);
  GRAPHICS_ShowStatus(si7013_status, false);
  sl_sleeptimer_delay_millisecond(2000);

  /* Set up periodic measurement timer */
  sl_sleeptimer_start_periodic_timer_ms(&measurement_timer, MEASUREMENT_INTERVAL_MS, measurement_callback, NULL, 0, 0);

  EMU_EnterEM2(false);

  while (true) {
    if (measurement_flag) {
      measure_humidity_and_temperature(i2cInit.port, &rhData, &tempData, &vBat);
      measurement_flag = false;
      if (lowBatPrevious) {
        lowBat = (vBat <= LOW_BATTERY_THRESHOLD);
      } else {
        lowBat = false;
      }
      lowBatPrevious = (vBat <= LOW_BATTERY_THRESHOLD);
      GRAPHICS_Draw(tempData, rhData, lowBat);
    }
    EMU_EnterEM2(false);
  }
}

/***************************************************************************//**
 * @brief Setup GPIO interrupt for pushbuttons.
 *****************************************************************************/
static void gpioSetup(void)
{
  /* Enable GPIO clock */
  CMU_ClockEnable(cmuClock_GPIO, true);

  /* Enable si7021 sensor isolation switch */
  GPIO_PinModeSet(gpioPortC, 8, gpioModePushPull, 1);
}

/***************************************************************************//**
 * @brief This function is called whenever we want to measure the supply v.
 *        It is reponsible for starting the ADC and reading the result.
 ******************************************************************************/
static uint32_t checkBattery(void)
{
  uint32_t vData;
  /* Sample ADC */
  adcConversionComplete = false;
  ADC_Start(ADC0, adcStartSingle);
  while (!adcConversionComplete) EMU_EnterEM1();
  vData = ADC_DataSingleGet(ADC0);
  return vData;
}

/***************************************************************************//**
 * @brief ADC Interrupt handler (ADC0)
 ******************************************************************************/
void ADC0_IRQHandler(void)
{
  uint32_t flags;

  /* Clear interrupt flags */
  flags = ADC_IntGet(ADC0);
  ADC_IntClear(ADC0, flags);

  adcConversionComplete = true;
}

/***************************************************************************//**
 * @brief ADC Initialization
 ******************************************************************************/
static void adcInit(void)
{
  ADC_Init_TypeDef       init       = ADC_INIT_DEFAULT;
  ADC_InitSingle_TypeDef initSingle = ADC_INITSINGLE_DEFAULT;

  /* Enable ADC clock */
  CMU_ClockEnable(cmuClock_ADC0, true);

  /* Initiate ADC peripheral */
  ADC_Init(ADC0, &init);

  /* Setup single conversions for internal VDD/3 */
  initSingle.acqTime = adcAcqTime16;
  initSingle.input   = adcSingleInpVDDDiv3;
  ADC_InitSingle(ADC0, &initSingle);

  /* Manually set some calibration values */
  ADC0->CAL = (0x7C << _ADC_CAL_SINGLEOFFSET_SHIFT) | (0x1F << _ADC_CAL_SINGLEGAIN_SHIFT);

  /* Enable interrupt on completed conversion */
  ADC_IntEnable(ADC0, ADC_IEN_SINGLE);
  NVIC_ClearPendingIRQ(ADC0_IRQn);
  NVIC_EnableIRQ(ADC0_IRQn);
}

/***************************************************************************//**
 * @brief  Helper function to perform data measurements.
 ******************************************************************************/
static void measure_humidity_and_temperature(I2C_TypeDef *i2c, uint32_t *rhData, int32_t *tData, uint32_t *vBat)
{
  *vBat = checkBattery();
  Si7013_MeasureRHAndTemp(i2c, SI7021_ADDR, rhData, tData);
}

/***************************************************************************//**
 * @brief Callback from timer used to initiate new measurement
 ******************************************************************************/
static void measurement_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
{
  (void) handle;
  (void) data;
  measurement_flag = true;
}

/***************************************************************************//**
 * @brief   The actual callback for Memory LCD toggling
 ******************************************************************************/
static void display_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
{
  (void)handle;
  (void)data;
  mem_lcd_callback_func(mem_lcd_callback_arg);
}

/***************************************************************************//**
 * @brief   Register a callback function at the given frequency.
 *
 * @param[in] pFunction  Pointer to function that should be called at the
 *                       given frequency.
 * @param[in] argument   Argument to be given to the function.
 * @param[in] frequency  Frequency at which to call function at.
 *
 * @return  Always return 0
 ******************************************************************************/
int rtcIntCallbackRegister(void (*pFunction)(void*),
                           void* argument,
                           unsigned int frequency)
{
  mem_lcd_callback_func = pFunction;
  mem_lcd_callback_arg  = argument;
  uint32_t ticks = sl_sleeptimer_get_timer_frequency() / frequency;
  sl_sleeptimer_start_periodic_timer(&display_timer, ticks, display_callback, NULL, 0, 0);

  return 0;
}

 

 

다시 코드에 대한 분석을 시작해보겠습니다.

 

adcInit();

 

이 코드는 ADC(아날로그 디지털 컨버터)를 초기화하는 과정입니다.

 

sl_sleeptimer_init();

 

이 코드는 sleeptimer를 초기화하는 과정입니다.

 

GRAPHICS_Init();

 

보드에 부착되어 있는 디스플레이를 사용하기 위해 초기화를 시키는 과정입니다.

 

I2CSPM_Init(&i2cInit);

 

이 보드에 있는 온도센서는 I2C 통신을 이용하여 온도에 있는 데이터를 보드로 보내게 됩니다. 그러기 위해서 I2C 통신을 하기 위해 초기화를 시키고 있는 과정입니다.

 

/* Get initial sensor status */

si7013_status = Si7013_Detect(i2cInit.port, SI7021_ADDR, NULL);

 

Si7013_Dectect라는 함수를 찾아보았습니다.

 

함수 Si7013_Dectect는 온도센서가 읽어올 데이터가 어디에 저장이 되는지, 읽어온 데이터의 길이가 어떻게 될 것인지, 온도센서가 write할 데이터는 어디에 저장이 되고 데이터의 길이는 어떻게 되는지에 대한 정보들이 있었고 I2C 통신을 통해 온도센서가 연결이 활성화되었는지 확인하는 기능의 함수였습니다.

 

GRAPHICS_ShowStatus(si7013_status, false);

 

그 다음으로 이 함수에 대해서 알아보았습니다.

 

이 함수는 온도센서의 상태에 대한 정보와 배터리에 대한 정보를 받아와서 해당되는 상태에 따라 디스플레이에 표시를 하는 함수였습니다. lowBattery가 1이면 아래의 사진처럼 디스플레이에 "LOW BATTERY!"라고 화면에 표시가 됩니다.

 

그리고 만약 온도센서가 확인이 되지 않은 상태이면 "Failed to detect si7021 sensor."라고 화면에 표시가 됩니다. 온도센서가 확인이 된 상태이면 "si7021 sensor ready. Demo 버전"이라고 화면에 아래 사진과 같이 표시가 됩니다.

 

 

 

sl_sleeptimer_delay_millisecond(2000);

 

이 함수는 2ms만큼의 시간 동안 지연시키는 함수입니다.

 

/* Set up periodic measurement timer */

sl_sleeptimer_start_periodic_timer_ms(&measurement_timer, MEASUREMENT_INTERVAL_MS, measurement_callback, NULL, 0, 0);

 

이 코드는 타이머에 관한 설정입니다.

 

EMU_EnterEM2(false);

 

저번 회로도 포스팅에서 EM2는 에너지 관리 모드 중 deep sleep mode라고 설명드렸습니다. 이 함수는 deep sleep mode로 들어가는 함수입니다.

이때까지는 대부분 필요한 기능들을 초기화, 세팅하는 부분들이었습니다. while 문에서부터 본격적으로 센서가 어떻게 동작하고 값을 어떻게 받아오는지 프로그래밍 되어 있습니다. 다음 포스팅에서 나머지 부분들을 설명해드리도록 하겠습니다. 감사합니다.

 

반응형

댓글