이때까지 humitemp 예제 코드를 분석하면서 필요한 기능들을 초기화, 세팅하는 부분까지 분석해보았습니다. while 문에서부터 본격적으로 센서가 어떻게 동작하고 값을 어떻게 받아오는지 프로그래밍 되어 있습니다. 아래는 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;
}
이제 본격적으로 코드 해석을 시작하겠습니다.
while (true) {
while 문의 괄호 안이 항상 1이므로 무한 루프를 돌게 됩니다.
if (measurement_flag) {
measurement_flag가 static volatile 형식으로 지정이 되어있습니다. 값은 true로 1로 지정이 되어있습니다.
그러므로 if의 괄호 안도 1이니 if문 안으로 들어가게 됩니다.
measure_humidity_and_temperature(i2cInit.port, &rhData, &tempData, &vBat);
이 함수는 습도와 온도를 측정하는 함수입니다. 이 함수가 어떻게 동작하는지 더 자세히 알아보기 위해 이 함수에 대해 찾아보았습니다.
이 함수의 첫 번째 줄부터 차근차근 해석해보도록 하겠습니다. 우선 checkBattery라는 함수를 들어가 봅니다.
checkBattery 함수를 들어가 보니 ADC(아날로그 디지털 컨버터) 기능을 하는 함수가 있었습니다. ADC_start는 ADC0을 사용합니다. 그러고 나서 adcConversionComplete가 0이면 EMU_EnterEM1을 실행하게 되는데 에너지 관리 모드 중 EM1은 sleep mode입니다. 그 후에 ADC0를 통해 받아온 값을 vData에 저장하고 이 값을 리턴하게 됩니다. 이 ADC 값은 vData에 저장이 되고 결과적으로는 measure_humidity_and_temperature 함수의 첫 번째 줄에 있는 변수인 vBat에 저장이 됩니다.
다시 돌아가서 이번에는 Si7013_MeasureRHAndTemp에 대해서 알아 보도록 합시다.
이 함수의 첫 번째 줄인 Si7013_Measure에 대해서 알아봅시다. 이 함수는 크게 두 파트로 나눌 수 있습니다. 첫 번째로 아래의 사진처럼 온도센서와의 I2C 통신을 통해 온도센서에 대한 데이터를 읽어와 저장하거나 데이터를 쓰는 과정이 있습니다.
두 번째로는 I2C 통신이 종료되었는지 확인하는 과정이 있습니다. 종료되었으면 데이터를 data 변수에 저장하고 2를 리턴합니다. 종료되지 않았으면 data에 0을 저장하고 ret을 리턴합니다.
다시 Si7013_MeasureRHAndTemp 함수로 돌아와서 if 문부터 보게 되면 ret이 2이면 측정한 데이터를 milli-percent로 바꿉니다. 또다시 Si7013_Measure 함수가 나오는데 아까는 SI7013_READ_RH command였다면 이번에는 SI7013_READ_TEMP로 command를 바꾸어 데이터 측정을 시작합니다. 그 밑의 if 문은 ret이 2이면 측정한 데이터를 milli-degC로 바꿉니다.
measurement_flag = false;
아까는 1이었던 measurement_flag를 0으로 바꾸어 줍니다.
if (lowBatPrevious) {
lowBatPrevious는 처음 시작할 때는 1로 값이 저장이 되어있으므로 처음에는 바로 if문 안으로 들어가게 됩니다.
lowBat = (vBat <= LOW_BATTERY_THRESHOLD);
LOW_BATTERY_THRESHOLD가 2800으로 설정이 되어있습니다. 2800 아래로는 배터리 잔량이 낮은 상태입니다. vBat과 비교하여 2800보다 낮으면 lowBat이 1이 됩니다.
} else {
lowBat = false;
}
lowBatPrevious가 1이 아니라면 lowBat에 0이 저장됩니다.
lowBatPrevious = (vBat <= LOW_BATTERY_THRESHOLD);
vBat과 비교하여 2800보다 작으면 lowBatPrevious에 1이 저장이 됩니다.
GRAPHICS_Draw(tempData, rhData, lowBat);
이 함수에 대해 알아보도록 하겠습니다.
lowBat이 1이면 "LOW BATTERY!"라고 디스플레이에 표시가 됩니다. lowBat이 0이면 측정한 온도값이 디스플레이에 표시가 됩니다.
EMU_EnterEM2(false);
이후 deep sleep 모드로 들어갑니다.
'전자공학 > LoRa 통신' 카테고리의 다른 글
EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(6)-UART ,tera term 응용 (0) | 2020.03.02 |
---|---|
EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(5)-UART 설명 및 Tera Term 사용법 (0) | 2020.03.01 |
EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(3) (0) | 2020.02.26 |
EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(2) (0) | 2020.02.25 |
EFM32 Starter Kit를 이용하여 Simplicity Studio 사용하기(1) (0) | 2020.02.24 |
댓글