8.5 TOTAL SCORE

nucleo-stm32c0316

Razvojni sistemi
Kvalitet izrade 10
Implementirana tehnologija 7
Ease-of-use 7
Odnos cena/performanse 10
PROS
  • M0+ jezgro na 48MHz
  • 32 bita po ceni 8-16 bitnih sistema
  • Power menadžent
  • ST-LINK/V2-1 in-circuit dibager i programator
CONS
  • Nema pripadajućeg USB kabla data kabla
Bottomline

Iskoristili smo trenutak i istražili najnoviji NUCLEO-STM32C0316 razvojni sistem. Izveden na srednjem formatu veličine NUCLEO-STM razvojnih ploča, daće obilje portova za povezivanje. Tu je sve što nam je bilo potrebno za izradu osnovne ideje IoT sistema za merenje vlage zemljišta koji se može koristiti u preciznoj poljoprivredi.

 

Cortex-M0+ je ušao u modu. Iako je inžinjersko rešenje staro preko decenije, doživljava svoj procvat u polju mikrokontrolera. Možda danas najpoznatiji mikrokontroler baziran na M0+ je RP2040 koga je Raspberry Pi dizajn tim načinio od dva M0+ jezgra, dodajući na njih interesantnu PIO state mašinu. Na kraju, dobili smo rešenje koje je sa pravom doživelo pravi procvat na tržištu, gde sa cenom od oko 1 dolara po MCU omogućava izradu razvojnih pločica u cenovnom opsegu od 3 do 10 dolara. Cenovno gde je i NUCLEO-C031C6.

Sa druge strane, veliki igrači – STM i TI – su se držali jednog jezgra (time obezbeđujući jedan I2C, IS2, dva UART, tajmere, DMA, power kontrolu i sve to mikrokontrolerom koji radi na do 48MHz . Idealno rešenje za low-cost IoT razvoj.

NUCLEO-C031C6

Kada STM to začini Arduino i Morpho konektorima, ulazi se u svet koji je bio, najviše zbog cene, ograničen na 8- i 16-bit mikrokontrolere. Sada više nema razloga da nove IoT aplikacije ne budu 32-bitne.

Box & NUCLEO-C031C6

Iskoristili smo trenutak i istražili najnoviji NUCLEO-C031C6 razvojni sistem. Izveden na srednjem formatu veličine NUCLEO-STM razvojnih ploča, daje obilje portova za povezivanje. Standardno odlično označena NUCLEO-C031C6 ploča, sa dobrom podrškom u literaturi omogućava brzo ulaženje u STM32C0 svet.

Kao ideja za prvi projekat kojim bi predstavili ovaj M0+ mikrokontroler, u par koraka napravili smo trenutno popularan „green“ projekat precizne poljoprivrede : merač vlažnosti zemljišta.

Bez želje da duboko ulazimo u tematiku, neke osnove su potrebne za razumevanje samog sistema. Senzor koji smo koristili je generični kapacitivni senzor V2.0 koji proizvode mnogi proizvođači, ali ga DFRobot obično brendira. Kapacitivni senzor procenu vlažnosti zemljišta zasniva na činjenici da vazduh ima dielektričnu konstantu 0, suve materije u zemlji je imaju u intervalu od 0 do 5, dok je dielektrična konstanta vode 80. Da bi se izvršila inicijalna kalibracija senzora, potrebno je očitati izmerenu vrednost senzora u vazduhu i vrednost senzora uronjenog u vodu. Vlažnost vazduha ćemo proglasiti za 0%, dok ćemo vodu tretirati kao 100% vlažnu. Ono što očekujemo je da je vlažnost zemljišta negde u ovim granicama.

I tu počinju realne muke koje prevazilaze ideju ovog uvodnog teksta. Zapreminski udeo čvrste materije u zemljištu varira, orijentaciono možemo tvrditi da je oko 50%. Ovo može i ne mora biti. Zemljište puno gline može imati ovaj udeo mnogo veći, dok dobro aerisani supstrati na bazi treseta uz dodatak zeolita, koje obično imamo u saksijama mogu imati daleko manji zapreminski udeo čvrste materije. Ovde pada na pamet jedna dobra ideja da se inicijalna kalibracija ne treba vršiti između vazduha i vode, već u između dehidriranog i potpuno vodom zasićenog supstrata.

STM32C0316 u radu

Znajući da je zapreminski udeo mešavine vode i vazduha drugih 50%, i da vazduh i suvo tlo imaju dielektričnu konstantnu do 5, jasno je da voda istiskajući vazduh iz zapremine povećava rezultujuću dielektričnu konstantu „sistema“, i time menja kapacitivnost. Merenjem te promene dolazimo do vlažnosti supstrata.

Nakon normalizacije očitanih vrednosti i standardnog postupka normiranja rezultata, koji smo mi izveli na uzorku od deset očitavanja, izrazili smo vlažnost tla u procentualnom opsegu od 1 do 100. Par dana rada pokazalo je da sistem funckcioniše, da se vlažnost vremenom smanjuje i da bi u nekom trenutku moglo da se signalizira potreba za zalivanjem saksije.

Ovaj projekat je samo poziv na razmišljanje o upotrebi low-cost IoT sistema, kao što je i ovaj NUCLEO-C031C6, u preciznoj poljoprivredi – u kontekstu izgradnje IoT sistema za kontinuirano praćenje vlažnosti tla prilikom uzgoja biljaka.

#include "main.h"
#include "TM1637.h"

ADC_HandleTypeDef hadc1;

const uint32_t numReadings = 10;

uint32_t readings[10];
uint32_t readIndex = 0;

uint32_t level = 0;
uint32_t total = 0;
uint32_t average = 0;
uint32_t firstreadings = 0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);

int main(void)
{
 HAL_Init();
 SystemClock_Config();

 MX_GPIO_Init();
 MX_ADC1_Init();
 HAL_ADC_Start(&hadc1);
 TM1637_SetBrightness(7);

  for (uint32_t thisReading = 0; thisReading < numReadings; thisReading++) {
      readings[thisReading] = 0;}

  while (1)
  {

    if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK ){
     level = HAL_ADC_GetValue(&hadc1);
    }

     total = total - readings[readIndex];
     readings[readIndex] = level;
     total = total + readings[readIndex];
     readIndex = readIndex + 1;

     if (readIndex >= numReadings) {
         readIndex = 0;
       }
     average = total / numReadings;
     TM1637_DisplayDecimal((2600-average)*6.7, 1);
  }
  
}


void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

 
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.LowPowerAutoPowerOff = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_1CYCLE_5;
  hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
  hadc1.Init.OversamplingMode = DISABLE;
  hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}


static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  HAL_GPIO_WritePin(Led_GPIO_Port, Led_Pin, GPIO_PIN_RESET);

  HAL_GPIO_WritePin(GPIOB, DATA_Pin|CLK_Pin, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = User_Button_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(User_Button_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = Led_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(Led_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = DATA_Pin|CLK_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF0_USART1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF6_I2C1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);

}


void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
 
}
#endif

Više informacija: https://www.st.com/en/evaluation-tools/nucleo-c031c6.html

Aleksandar Dakić
Follow me