61

Re: STM32, Atolic TrueStudio, CubeMX

taburyak написав:

Колись давно шановний VitekSVM, свого часу, дуже допоміг з цим меню розібратись, хоч на той час в мене так і не вийшло.

VitekSVM написав:
#define MENU_ITEM_READ_POINTER(Addr)   (void*)pgm_read_word(Addr)

замінити на

#define MENU_ITEM_READ_POINTER(Addr)   (void*)(Addr)

Щось тут не так.
З першим рядком для AVR все добре. pgm_read_word приймає адресу об'єкта, але повертає uint16_t. Ми у цьому макросі знаємо, що читаємо вказівник, тому приводимо отриманий uint16_t до (void*).

А от з другим рядком біда. Макрос приймає таку саму адресу об'єкта у пам'яті, але замість прочитати значення просто приводить цю адресу до (void*).
Відповідно тут

taburyak написав:
void (*SelectCallback)(void) = MENU_ITEM_READ_POINTER(&CurrentMenuItem->SelectCallback);

    if (SelectCallback)
        SelectCallback();

Відбувається «виклик» не по адресі функції, а по адресі даних, того місця, де у структурах меню розміщено адресу функції. Тобто далі виконується випадковий код.

І тому треба саме так

taburyak написав:
#define MENU_ITEM_READ_POINTER(Addr)   *(Addr)

Тут макрос отримує адресу і звертається по ній. Оскільки ми знаємо, що там у комірці лежить теж адреса, ніякі приведення не потрібні. Зрештою, якби писалося відразу для мікроконтролера з єдиним простором адрес, то і макрос не був би потрібен. То для AVR через необхідність звертання до флеша іншими командами довелося загорнути у макрос, щоб не виписувати кожен раз pgm_read_word, що не таке зрозуміле при наступних читаннях коду, як самодокументоване ім'я макроса.

taburyak написав:

Хтось в курсі чому так відбувається? Ну або чим запис (void*)(Addr) відрізняється від *(Addr)?

Якщо викласти сказане вище коротко, то (void*)(Addr) — приведення параметра Addr до типу void*, а друге — зчитування того, що лежить у комірці з адресою Addr

Подякували: taburyak1

62

Re: STM32, Atolic TrueStudio, CubeMX

p.s. От щоб два макроса були зовсім еквівалентними, то треба б записати так

#define MENU_ITEM_READ_POINTER(Addr)   *(void*)(Addr)

Бо у варіанті AVR макросу pgm_read_word начхати, адресу якого типу йому підсунули. Він все одно читає 16-бітне слово, яке потім приводиться до void*.
Але тут знову «ми знаємо», що Addr є адресою та ще й потрібного типу. Ця повна еквівалентність непотрібна, оскільки тут зчитується з адреси &CurrentMenuItem->SelectCallback. Для «нормальнішого» за AVR процесора взагалі можна було б написати

void (*SelectCallback)(void) = CurrentMenuItem->SelectCallback;

але скрізь бігати і замінювати макрос MENU_ITEM_READ_POINTER може бути ліньки.
Тому годиться просто *(Addr), яке розгортається у *(&CurrentMenuItem->SelectCallback), тобто фактично нульова операція.
Для AVR спочатку прочитане слово привели до void*, потім void* автоматично і мовчки привелося до вказівника на функцію.

Подякували: taburyak1

63

Re: STM32, Atolic TrueStudio, CubeMX

ReAl, велике дякую. Дуже поміг розібратись. З вказівниками в мене виникають ще деякі труднощі, але на загал зрозумів. Привів micromenu-2 до "нормального" мікроконтролера. Все працює як слід.

64

Re: STM32, Atolic TrueStudio, CubeMX

Я тут нашкрябав про RTC на сотій серії stm32f1xx http://stm32withoutfear.blogspot.com/20 … 2f1xx.html. На hal теж працює повноцінно, хоча і для цього в жертву два backup регістра приніс. Ще додам пізніше про alarm.

Подякували: ReAl1

65

Re: STM32, Atolic TrueStudio, CubeMX

Я за звичкою роблю все врукопашну, без усіх цих Cube.
Звернув увагу на це:

Структура з налаштуваннями RTC.

/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef hrtc;

То воно що, ці змінні на файловому рівні заводить?
Це ж даремні витрати RAM, для декількох ініціалізацій різної периферії вони не перекриються, так всі поруч і сидітимуть.

Краще б так все, тоді вони на стекові будуть:

static void MX_RTC_Init(void)
{
  RTC_HandleTypeDef hrtc;
    /*Initialize RTC Only*/
  hrtc.Instance = RTC;
  …

Інші структури також втягти у функції, які їх використовують

RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
Подякували: taburyak1

66

Re: STM32, Atolic TrueStudio, CubeMX

Я за звичкою роблю все врукопашну, без усіх цих Cube.

Поважаю ;-)

Звернув увагу на це

О, дякую що підняв цю тему. Пропонуєш кожного разу по потребі в функціях оголошувати потрібні структури чи там змінні? Тоді виходить, що після виходу з функції, ті оголошені структури чи змінні типу "щезнуть".
Я щоб уточнити, бо ж в С відносний новачок :)
Візьму на олівець. Спробую твою пораду.

67

Re: STM32, Atolic TrueStudio, CubeMX

taburyak написав:

Я за звичкою роблю все врукопашну, без усіх цих Cube.

Поважаю ;-)

Та просто для ефективного використання периферії, на рівні, глибшому за спільну абстракцію у різних мікроконтролерах, її опис читати однак потрібно. Після цього нема проблем врукопашну ініціалізацію написати, особливо враховуючи, що обсяг писанини однаковий що із заповненням структур та викликом функцій, що отак (непотрібні поля теж відразу пишуться, просто множаться на 0 і потім це піде як заготовка в інше місце):

    // --------- DMA setup -----------
    DMA2_Channel3->CPAR  = uintptr_t(&DAC->DHR12RD);
    DMA2_Channel3->CMAR  = uintptr_t(pbuf);
    DMA2_Channel3->CNDTR = len;
    DMA2_Channel3->CCR = 0
        | 1 * DMA_CCR3_EN       // Channel enable
        | 1 * DMA_CCR3_TCIE     // Transfer complete interrupt enable
        | 1 * DMA_CCR3_HTIE     // Half Transfer interrupt enable
        | 0 * DMA_CCR3_TEIE     // Transfer error interrupt enable
        | 1 * DMA_CCR3_DIR      // Data transfer direction: Memory->Peripheral
        | 1 * DMA_CCR3_CIRC     // Circular mode
        | 0 * DMA_CCR3_PINC     // Peripheral increment mode
        | 1 * DMA_CCR3_MINC     // Memory increment mode
        | 2 * DMA_CCR3_PSIZE_0  // Peripheral size: 32 bits
        | 2 * DMA_CCR3_MSIZE_0  // Memory size: 32 bits
        | 0 * DMA_CCR3_PL_0     // Channel Priority level:  lowest, conversion frequency is low enough
        | 0 * DMA_CCR3_MEM2MEM  // Memory to memory mode:   disabled
        ;

А коду згенерується ще й менше. Ну код мо й хай, а от в обробнику переривань написати

    if(DMA2->ISR & DMA_ISR_HTIF3)
    {
        if(done_callback != NULL)
            done_callback(0);
        DMA2->IFCR = DMA_IFCR_CHTIF3;
    }
    if(DMA2->ISR & DMA_ISR_TCIF3)
    {
        if(done_callback != NULL)
            done_callback(1);
        DMA2->IFCR = DMA_IFCR_CTCIF3;
    }

економить під час виконання купу часу у порівнянні з викликом функцій.

taburyak написав:

О, дякую що підняв цю тему. Пропонуєш кожного разу по потребі в функціях оголошувати потрібні структури чи там змінні? Тоді виходить, що після виходу з функції, ті оголошені структури чи змінні типу "щезнуть".

Саме так. На стекові резервується місце, заповнюються поля, виклик функції і потім стек очищується. Виклик наступної функції-ініціалізатора — аналогічно. Оперативки під стек потрібно MAX(розміри структур), а так, як зараз — SUM(розміри структур).

Подякували: 0xDADA11C7, taburyak2

68

Re: STM32, Atolic TrueStudio, CubeMX

Після цього нема проблем врукопашну ініціалізацію написати

Дякую за приклад. Я ще такого ніколи не бачив і ніде не зустрічав. Це настільки для мене по-новому (ні, про CMSIS і документацію я в курсі), що ще треба це переварити і усвідомити :-)

69 Востаннє редагувалося ReAl (14.01.2017 12:12:31)

Re: STM32, Atolic TrueStudio, CubeMX

taburyak написав:

Дякую за приклад. Я ще такого ніколи не бачив і ніде не зустрічав.

Ця форма запису народилася десь в глибинах electronix.ru, куди я не ходець десь з другої половини 2012-го.
Погугліть ще «макроси Волкова» для роботи з портами, вони взагалі десь ще в кінці 1990-х в RU.EMBEDDED для AVR робилися, спочатку простіші, потім отримали розвиток. Пізніше схоже було зроблене для MSP430 і наче для STM32 теж. Все на макросах, які мають доволі зручний запис при роботі і розгортаються в мінімальний код.

Потім під їхнім впливом і практично тими ж людьми на С++ написано шаблонні класи для роботи з портами (ARM7, Cortex), теж зручний запис і все inline. То було під час роботи над scmRTOS і там і шукати.

p.s. Форма запису з 0 після знаку = і з ; окремим рядком — від початкового варіанту, коли непотрібний рядок, що починається з |, просто закоментовувався. Тоді можна закоментувати все і все буде коректний вираз зі значенням 0.

Подякували: 0xDADA11C7, taburyak2

70

Re: STM32, Atolic TrueStudio, CubeMX

Привіт друзі!

Допоможіть, гляньте свіжим оком, наче все роблю вірно, а не працює як слід. Мова про RTC на HAL для STM32F1xx.

Є пристрій з RTC і під'єднаним резервним живленням VBAT. В документації до HAL зазначено, що на STM32F1xx RTC з "особливостями" і є SW (софтварним). Що "йде" лічильник часу в межах доби, а про дату треба самому потурбуватись перед переходом в сон, а коли "прокинулись", то необхідно відновити дату з backup регістрів, викликати функцію HAL_RTC_GetTime, або HAL_RTC_GetData, щоб отримати час і дату, та додати за допомоги внутрішньої функції RTC_DateUpdate, до поточної дати кількість днів, протягом яких пристрій був у відключці.

Дивлюсь на ці функції і наче все вірно. Викликав HAL_RTC_GetTime, за допомоги внутрішньої функції RTC_ReadTimeCounter отримую кількість секунд в лічильнику, вираховую години, хвилини і секунди, та додаю за допомоги функції RTC_DateUpdate кількість днів, якщо кількість секунд перевищили добу. У функції HAL_RTC_GetData викликається функція HAL_RTC_GetTime і відбувається все те саме. В доках так і зазначено що викликати потрібно або ту, або ту функцію і перехід на нову добу врахується.

Прихований текст

##### WARNING: Drivers Restrictions  #####
  ==================================================================
  [..] RTC version used on STM32F1 families is version V1. All the features supported by V2
       (other families) will be not supported on F1.
  [..] As on V2, main RTC features are managed by HW. But on F1, date feature is completely
       managed by SW.
  [..] Then, there are some restrictions compared to other families:
    (+) Only format 24 hours supported in HAL (format 12 hours not supported)
    (+) Date is saved in SRAM. Then, when MCU is in STOP or STANDBY mode, date will be lost.
        User should implement a way to save date before entering in low power mode (an
        example is provided with firmware package based on backup registers)
    (+) Date is automatically updated each time a HAL_RTC_GetTime or HAL_RTC_GetDate is called.
    (+) Alarm detection is limited to 1 day. It will expire only 1 time (no alarm repetition, need
        to program a new alarm)

Насправді, працює так. Виставляю час 23:59:30, час починає йти, вимикаю пристрій (основне живлення), через пару хвилин вмикаю, все гаразд, до дати додався один день, час йде вірно. Це при умові, коли першою викликати функцію HAL_RTC_GetTime. Якщо викликати HAL_RTC_GetDate, то новий день не додається до дати. Хоча має додатись.
А також якщо пристрій залишити без живлення більш ніж на добу, то при увімкнені його взагалі не додається днів, а світить датою та що була на момент вимкнення. Хоча при вимкнені пристрою RTC лічильник секунд просто рахує секунди і все, а коли увімкнули пристрій і викликали функцію HAL_RTC_GetTime або HAL_RTC_GetDate то надлишок секунд в добі порахується в кількість днів які минули у вимкненому стані.
Я не можу зрозуміти в чому проблема. Приклад як роблю обробку RTC тут http://stm32withoutfear.blogspot.com/20 … 2f1xx.html

Для зручності додав під спойлер функції HAL_RTC_GetTime, HAL_RTC_GetDate, RTC_ReadTimeCounter та RTC_DateUpdate

Прихований текст
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
{
  uint32_t counter_time = 0, counter_alarm = 0, days_elapsed = 0, hours = 0;
  
  /* Check input parameters */
  if((hrtc == NULL) || (sTime == NULL))
  {
     return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_RTC_FORMAT(Format));

  /* Check if counter overflow occurred */
  if (__HAL_RTC_OVERFLOW_GET_FLAG(hrtc, RTC_FLAG_OW))
  {
      return HAL_ERROR;
  }

  /* Read the time counter*/
  counter_time = RTC_ReadTimeCounter(hrtc);

  /* Fill the structure fields with the read parameters */
  hours = counter_time / 3600;
  sTime->Minutes  = (uint8_t)((counter_time % 3600) / 60);
  sTime->Seconds  = (uint8_t)((counter_time % 3600) % 60);

  if (hours >= 24)
  {
    /* Get number of days elapsed from last calculation */
    days_elapsed = (hours / 24);

    /* Set Hours in RTC_TimeTypeDef structure*/
    sTime->Hours = (hours % 24);    

    /* Read Alarm counter in RTC registers */
    counter_alarm = RTC_ReadAlarmCounter(hrtc);

    /* Calculate remaining time to reach alarm (only if set and not yet expired)*/
    if ((counter_alarm != RTC_ALARM_RESETVALUE) && (counter_alarm > counter_time))
    {
      counter_alarm -= counter_time;
    }
    else 
    {
      /* In case of counter_alarm < counter_time */
      /* Alarm expiration already occurred but alarm not deactivated */
      counter_alarm = RTC_ALARM_RESETVALUE;
    }

    /* Set updated time in decreasing counter by number of days elapsed */
    counter_time -= (days_elapsed * 24 * 3600);
    
    /* Write time counter in RTC registers */
    if (RTC_WriteTimeCounter(hrtc, counter_time) != HAL_OK)
    {
      return HAL_ERROR;
    }

    /* Set updated alarm to be set */
    if (counter_alarm != RTC_ALARM_RESETVALUE)
    {
      counter_alarm += counter_time;
      
      /* Write time counter in RTC registers */
      if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
      {
        return HAL_ERROR;
      }
    }
    else
    {
      /* Alarm already occurred. Set it to reset values to avoid unexpected expiration */
      if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
      {
        return HAL_ERROR;
      }
    }
    
    /* Update date */
    RTC_DateUpdate(hrtc, days_elapsed);
  }
  else 
  {
    sTime->Hours = hours;    
  }

  /* Check the input parameters format */
  if(Format != RTC_FORMAT_BIN)
  {
    /* Convert the time structure parameters to BCD format */
    sTime->Hours    = (uint8_t)RTC_ByteToBcd2(sTime->Hours);
    sTime->Minutes  = (uint8_t)RTC_ByteToBcd2(sTime->Minutes);
    sTime->Seconds  = (uint8_t)RTC_ByteToBcd2(sTime->Seconds);  
  }
  
  return HAL_OK;
}
Прихований текст
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
{
  RTC_TimeTypeDef stime = {0};
  
  /* Check input parameters */
  if((hrtc == NULL) || (sDate == NULL))
  {
     return HAL_ERROR;
  }
  
  /* Check the parameters */
  assert_param(IS_RTC_FORMAT(Format));
  
  /* Call HAL_RTC_GetTime function to update date if counter higher than 24 hours */
  if (HAL_RTC_GetTime(hrtc, &stime, RTC_FORMAT_BIN) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* Fill the structure fields with the read parameters */
  sDate->WeekDay  = hrtc->DateToUpdate.WeekDay;
  sDate->Year     = hrtc->DateToUpdate.Year;
  sDate->Month    = hrtc->DateToUpdate.Month;
  sDate->Date     = hrtc->DateToUpdate.Date;

  /* Check the input parameters format */
  if(Format != RTC_FORMAT_BIN)
  {    
    /* Convert the date structure parameters to BCD format */
    sDate->Year   = (uint8_t)RTC_ByteToBcd2(sDate->Year);
    sDate->Month  = (uint8_t)RTC_ByteToBcd2(sDate->Month);
    sDate->Date   = (uint8_t)RTC_ByteToBcd2(sDate->Date);  
  }
  return HAL_OK;
}
Прихований текст
static void RTC_DateUpdate(RTC_HandleTypeDef* hrtc, uint32_t DayElapsed)
{
  uint32_t year = 0, month = 0, day = 0;
  uint32_t loop = 0;

  /* Get the current year*/
  year = hrtc->DateToUpdate.Year;

  /* Get the current month and day */
  month = hrtc->DateToUpdate.Month;
  day = hrtc->DateToUpdate.Date;

  for (loop = 0; loop < DayElapsed; loop++)
  {
    if((month == 1) || (month == 3) || (month == 5) || (month == 7) || \
       (month == 8) || (month == 10) || (month == 12))
    {
      if(day < 31)
      {
        day++;
      }
      /* Date structure member: day = 31 */
      else
      {
        if(month != 12)
        {
          month++;
          day = 1;
        }
        /* Date structure member: day = 31 & month =12 */
        else
        {
          month = 1;
          day = 1;
          year++;
        }
      }
    }
    else if((month == 4) || (month == 6) || (month == 9) || (month == 11))
    {
      if(day < 30)
      {
        day++;
      }
      /* Date structure member: day = 30 */
      else
      {
        month++;
        day = 1;
      }
    }
    else if(month == 2)
    {
      if(day < 28)
      {
        day++;
      }
      else if(day == 28)
      {
        /* Leap year */
        if(RTC_IsLeapYear(year))
        {
          day++;
        }
        else
        {
          month++;
          day = 1;
        }
      }
      else if(day == 29)
      {
        month++;
        day = 1;
      }
    }
  }

  /* Update year */
  hrtc->DateToUpdate.Year = year;

  /* Update day and month */
  hrtc->DateToUpdate.Month = month;
  hrtc->DateToUpdate.Date = day;

  /* Update day of the week */
  hrtc->DateToUpdate.WeekDay = RTC_WeekDayNum(year, month, day);
}
Прихований текст
static uint32_t RTC_ReadTimeCounter(RTC_HandleTypeDef* hrtc)
{
  uint16_t high1 = 0, high2 = 0, low = 0;
  uint32_t timecounter = 0;

  high1 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);
  low   = READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT);
  high2 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);

  if (high1 != high2)
  { /* In this case the counter roll over during reading of CNTL and CNTH registers, 
       read again CNTL register then return the counter value */
    timecounter = (((uint32_t) high2 << 16 ) | READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT));
  }
  else
  { /* No counter roll over during reading of CNTL and CNTH registers, counter 
       value is equal to first value of CNTL and CNTH */
    timecounter = (((uint32_t) high1 << 16 ) | low);
  }

  return timecounter;
}
Подякували: 0xDADA11C71

71 Востаннє редагувалося ReAl (20.01.2017 20:51:16)

Re: STM32, Atolic TrueStudio, CubeMX

Що під спойлерами — не читав :-)
Документацію на Cube/HAL — не читав :-)
Читав документацію на RTC STM32F107 (годинник в інших STM32 мене не цікавив, тому не читав, не порівнював детально).
Тому у мене годинник STM32F107 працює в unixtime. Через це маю проблему 2038, яку поки розв'язувати не збираюся. STM32F107 тоді вже не випускатиметься.
Викликається якось так

    time_t tim = rtc.get_time();
    debug_console << "\trtc: "  << asctime(gmtime(&tim)) << VT100_CLR_EOL "\r\n";
тут не все і вичищено mutex-и від scmRTOS, але то не важливо
rtc_t::rtc_t()
    : rtc_corrupted(false) // ПДН :-)
{
    RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN);
    PWR->CR |= PWR_CR_DBP;

    if (RCC->BDCR != (RCC_BDCR_LSEON | RCC_BDCR_LSERDY | RCC_BDCR_RTCSEL_LSE | RCC_BDCR_RTCEN)
        || BKP->DR1 != rtc_signature
        )
    {
        // pins OSC32_IN/OSC32_OUT після ввімкнення "аналогові", LSEON має пріоритет
        RCC->BDCR = RCC_BDCR_BDRST;
        rtc_corrupted = true;

        RCC->BDCR = RCC_BDCR_LSEON;
        while (!(RCC->BDCR & RCC_BDCR_LSERDY)) ;

        RCC->BDCR = RCC_BDCR_LSEON | RCC_BDCR_RTCSEL_LSE;
        RCC->BDCR = RCC_BDCR_LSEON | RCC_BDCR_RTCSEL_LSE | RCC_BDCR_RTCEN;

        // Почекати можливості зчитування та запису
        wait_rsf();
        config_start();
        RTC->PRLH = 0;
        RTC->PRLL = (1<<15) - 1;
        RTC->CNTH = 0;
        RTC->CNTL = 0;
        config_end();
    }
}

void rtc_t::wait_rsf()
{
    RTC->CRL &= uint16_t(~RTC_FLAG_RSF);
    while (!(RTC->CRL & RTC_CRL_RSF)) ;
}

// FIXME: Очікування RTOFF може бути потрібним не лише для того, що під CNF ?
void rtc_t::config_start()
{
    while (!(RTC->CRL & RTC_FLAG_RTOFF)) ;
    RTC->CRL |= RTC_CRL_CNF;
}

void rtc_t::config_end()
{
    RTC->CRL &= ~RTC_CRL_CNF;
    while (!(RTC->CRL & RTC_FLAG_RTOFF)) ;
}


time_t rtc_t::get_time()
{
    // wait_rsf() іноді тягне і 35 мкс -> ~2500 тактів!
    // Але це викликається дуже рідко, то можна і так
    wait_rsf();
    uint16_t lo = RTC->CNTL;
    uint16_t hi = RTC->CNTH;
    return (time_t(hi) << 16) | lo;
}


void rtc_t::set_time(time_t tim)
{
    config_start();
    RTC->CNTL = uint16_t(tim);
    RTC->CNTH = uint16_t(tim >> 16);
    config_end();
    BKP->DR1 = rtc_signature;
    rtc_corrupted = false;
}
Подякували: 0xDADA11C7, taburyak2

72 Востаннє редагувалося taburyak (20.01.2017 21:16:25)

Re: STM32, Atolic TrueStudio, CubeMX

ReAl написав:

Тому у мене годинник STM32F107 працює в unixtime. Через це маю

Та то я в курсі. Маю реалізацію такого годинника. Можна віднімати кількість днів які минули з 1.1.1970 по 1.1.2001 і відтермінувати проблему переповнення на дуже далекий час :)

Мене цікавить саме на HAL порішати цю проблему. Знаю що іншим буде ліньки розбиратись, скоріше для себе пишу, щоб самому було не ліньки :)))

Здається я зрозумів в чому проблему.

Коли я відновлюю значення дати з backup регістрів і записую їх до структури дати та викликаю функцію HAL_RTC_SetDate:

Прихований текст
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
{
  uint32_t counter_time = 0, counter_alarm = 0, hours = 0;
  
  /* Check input parameters */
  if((hrtc == NULL) || (sDate == NULL))
  {
     return HAL_ERROR;
  }
  
  /* Check the parameters */
  assert_param(IS_RTC_FORMAT(Format));
  
 /* Process Locked */ 
 __HAL_LOCK(hrtc);
  
  hrtc->State = HAL_RTC_STATE_BUSY; 
  
  if(Format == RTC_FORMAT_BIN)
  {   
    assert_param(IS_RTC_YEAR(sDate->Year));
    assert_param(IS_RTC_MONTH(sDate->Month));
    assert_param(IS_RTC_DATE(sDate->Date)); 

    /* Change the current date */
    hrtc->DateToUpdate.Year  = sDate->Year;
    hrtc->DateToUpdate.Month = sDate->Month;
    hrtc->DateToUpdate.Date  = sDate->Date;
  }
  else
  {   
    assert_param(IS_RTC_YEAR(RTC_Bcd2ToByte(sDate->Year)));
    assert_param(IS_RTC_MONTH(RTC_Bcd2ToByte(sDate->Month)));
    assert_param(IS_RTC_DATE(RTC_Bcd2ToByte(sDate->Date)));
    
    /* Change the current date */
    hrtc->DateToUpdate.Year  = RTC_Bcd2ToByte(sDate->Year);
    hrtc->DateToUpdate.Month = RTC_Bcd2ToByte(sDate->Month);
    hrtc->DateToUpdate.Date  = RTC_Bcd2ToByte(sDate->Date);
  }

  /* WeekDay set by user can be ignored because automatically calculated */
  hrtc->DateToUpdate.WeekDay = RTC_WeekDayNum(hrtc->DateToUpdate.Year, hrtc->DateToUpdate.Month, hrtc->DateToUpdate.Date);
  sDate->WeekDay = hrtc->DateToUpdate.WeekDay;

  /* Reset time to be aligned on the same day */
  /* Read the time counter*/
  counter_time = RTC_ReadTimeCounter(hrtc);

  /* Fill the structure fields with the read parameters */
  hours = counter_time / 3600;
  if (hours > 24)
  {
    /* Set updated time in decreasing counter by number of days elapsed */
    counter_time -= ((hours / 24) * 24 * 3600);
    /* Write time counter in RTC registers */
    if (RTC_WriteTimeCounter(hrtc, counter_time) != HAL_OK)
    {
      /* Set RTC state */
      hrtc->State = HAL_RTC_STATE_ERROR;
      
      /* Process Unlocked */ 
      __HAL_UNLOCK(hrtc);
      
      return HAL_ERROR;
    }

    /* Read current Alarm counter in RTC registers */
    counter_alarm = RTC_ReadAlarmCounter(hrtc);

    /* Set again alarm to match with new time if enabled */
    if (counter_alarm != RTC_ALARM_RESETVALUE)
    {
      if(counter_alarm < counter_time)
      {
        /* Add 1 day to alarm counter*/
        counter_alarm += (uint32_t)(24 * 3600);
        
        /* Write new Alarm counter in RTC registers */
        if (RTC_WriteAlarmCounter(hrtc, counter_alarm) != HAL_OK)
        {
          /* Set RTC state */
          hrtc->State = HAL_RTC_STATE_ERROR;
          
          /* Process Unlocked */ 
          __HAL_UNLOCK(hrtc);
          
          return HAL_ERROR;
        }
      }
    }
    

  }

  hrtc->State = HAL_RTC_STATE_READY ;
  
  /* Process Unlocked */ 
  __HAL_UNLOCK(hrtc);
  
  return HAL_OK;    
}

то у лічильнику RTC секунд стає в межах доби не більше 86400, а потім вже викликай функцію RTC_HAL_GetTime чи ні, а днів, що минули, не додасця бо вже їх відняли від лічильника RTC.

Це тепер як робить щоб врахувати дні що минули? Буду розбиратись.

Подякували: ReAl1

73

Re: STM32, Atolic TrueStudio, CubeMX

taburyak написав:

Мене цікавить саме на HAL порішати цю проблему. Знаю що іншим буде ліньки розбиратись, скоріше для себе пишу, щоб самому було не ліньки :)))

Це точно. Буде ліньки :)
Через те багатослів'я ліньки продиратися.

Що ж вони там по if (hours > 24) від лічильника секунд віднімають, але у дату не додають?

74 Востаннє редагувалося taburyak (20.01.2017 21:49:18)

Re: STM32, Atolic TrueStudio, CubeMX

ReAl написав:

Що ж вони там по if (hours > 24) від лічильника секунд віднімають, але у дату не додають?

Саме так. Та таке, на голову не натягнеш. Один, колись кляв той HAL, на чому світ стоїть. Здається я скоро теж приєднаюсь до того одного :)

Головне що знайшов причину. Вже придумаю як зробити.

p.s. це для статті про RTC, а собі на пристрій, звісно, зроблю типу unix_time, тільки рахувати з 1/1/2001 буду.

75

Re: STM32, Atolic TrueStudio, CubeMX

Та ото.
Я з LPC1768 був почав через HAL. Ну там «нове життя», «досить витрачати свій час на ручне колупання у регістрах», «та тут у рази більше пам'яті й у десятки вища швидкість, ніж то у „Електроніки-60“ було, а вони ж цехами керували» і так далі.
Брався за платку зрідка, нічого обов'язкового не було (замовник згорнувся, плата залишилася), тому вряди-годи сідав погратися і вперто зі словами «може я просто ще не розібрав кайф від цього» писав через їхню «Peripheral Driver Library». Але отаке і отаке переконали мене, що краще по старому.

Тобто або має бути операційна система, яка за собою ховає взагалі все, або я працюю з нижнім рівнем вручну.

Подякували: taburyak, 0xDADA11C72

76

Re: STM32, Atolic TrueStudio, CubeMX

Зробив таки годинника на HAL. Спочатку з лічильника RTC час з датою до тимчасової структури дати, а потім збережену дату до основної структури дати, та й додаю тимчасову дату (дні що минули у "відключці") до основної. Тепер працює як слід.

Подякували: ReAl1

77 Востаннє редагувалося taburyak (17.11.2017 10:48:23)

Re: STM32, Atolic TrueStudio, CubeMX

Всім привіт!

Зачепився я цим UART на STM32 - це якийсь триндєць, або я щось принципово не доганяю.

Використовую CubeMX + HAL + TrueStudio.
В mbed compiller наче з цим полегше, але отє "слабоє подобіє ардуіно" мене не цікавить. І щось побачити там на UART в мене так і не вийшло.

Передачу організувати доволі легко. Тут питань немає. Через DMA шлю рядок "Hello World!" в TX UARTу, аж спінджак завертається. На ПК в терміналі бачу той же ж ""Hello World!". Дуже з того радію.

Дійшло діло до RX. А тут, як виявилось, не все так просто. В чому біда? Якщо очікувати на прийом якийсь конкретний розмір рядка, то теж наче-то просто. Буфер заповнився, DMA ініціювало переривання, там собі прапорець скинув і з буферу забрав рядок. Але коли наперед не знаєш якої довжини буде рядок, тоді треба брати по одному байту складувати то все десь окремо і аналізувати те окреме. І якщо по перериванню по одному байту, то є великий шанс пропустити байти. Тоді можна по одному байту з DMA (таке собі збочення) і вже складати ті байти окремо і аналізувати.

Навіть познаходив якісь реалізації цього (опис проблеми і її рішення) https://stackoverflow.com/questions/248 … hal-driver

Я собі підглянув туди і зробив собі. Але щось не дуже вийшло. В терміналі на ПК передаю на STM32 рядок, а у відповідь (зробив типу відлуння) отримую тільки перший символ рядку. Ну і все. Думаю я щось не доганяю принципово з цими UARTами.

На загал, я хотів до STM32 по UART підключити WiFi модем ESP8266 і AT командами з STM32 керувати тим модемом. Задача мінімум, хоча б мати можливість відсилати на ESP8266 AT команди і отримувати назад відповіді від ESP8266. Хоч поки я не розумію що далі з тим робити, якщо ця задача мінімум вийде. Це треба якийсь TCP/IP стек організовувати на STM32, чи то він вже є на ESP8266? Темний ліс.

Якихось прикладів, чи то готових рішень, в тому вашому інтернеті, я не знайшов. Може хто вже має досвід з цим, та направить на шлях істинний?

Ще загальніше, то я зачепився з тими ESP8266 не як модем, а як окремий мікроконтролер з своєю програмою для потреб IoT в системі blynk https://www.blynk.cc/. Все там добре в мене виходить. Але буває можливостей того чипу замало і хотілось би використовувати потужніший мікроконтролер, а ESP8266 як модем.
Є ще думка ESP8266 використовувати як мікроконтролер зі зв'язком WiFi, що в мене вже виходить. А потужніший мікроконтролер підключати до ESP8266 по SPI і вже по ньому ганяти потрібні дані. Але я той SPI ще не пробував ніколи. То теж з нуля треба розбиратись. Що думаєте?

78

Re: STM32, Atolic TrueStudio, CubeMX

Ну що ж. Більше конкретики додам.

Ось мій код, який стосується діла. Всілякі там ініціалізації периферії для справи зайві.

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    flagTxEnd = false; // коли закінчилась передача скидаємо прапорець
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    flagTxEnd = true; // коли закінчився прийом встановлюємо прапорець
}

void DMA1_Channel5_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma_usart1_rx); // сюди потрапляємо кожного разу коли прийнято половину і весь очікуваний рядок
}

void DMA1_Channel4_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma_usart1_tx); // сюди потрапляємо як передали половину і весь рядок
}

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1); // сюди потрапляємо після передачі всього рядка
}

int main(void)
{
    char str[11]={0};

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();

  while (1)
  {
      str[10] = 0;
      HAL_UART_Receive_DMA( &huart1, (uint8_t*) str, 10 ); // приймаємо десять байт по UART1

      if(flagTxEnd)
      {
          HAL_UART_Transmit_DMA( &huart1, (uint8_t*) str, 10 ); // передаємо прийняті десять байт по UART1
      }
  }

}

Оцє такоє поки що.

І от виходить що поки я не прийму 10 байт (розмір взято зі стелі для тестування) я не знаю як мені обробляти прийнятий рядок? Наприклад надіслати модему команду AT і отримати від нього відповідь. Як?

79

Re: STM32, Atolic TrueStudio, CubeMX

В черговий раз поговорю сам з собою. А що робить?

Розібрався, вийшло: http://stm32withoutfear.blogspot.com/20 … -uart.html.

Одне не зрозумів, чому коли функцію:

void print(char string[])
{
 HAL_UART_Transmit(&huart2, (uint8_t*) string, strlen(string), 10);
}

Міняю на:

void print(char string[])
{
 HAL_UART_Transmit_DMA(&huart2, (uint8_t*) string, strlen(string));
}

Перестає друкувати в термінал. Хоча якщо замість використання функції print, наявно передавати рядки  наприклад: HAL_UART_Transmit_DMA(&huart2, (uint8_t*) "\r\nMyHomeIoT> ", 13));
То все друкується.

Подякували: leofun011

80 Востаннє редагувалося taburyak (09.01.2018 15:03:57)

Re: STM32, Atolic TrueStudio, CubeMX

Вітання.
Є потреба перековбасити одну невеличку бібліотеку з Сі++ на Сі. Це бібліотека роботи Arduino з ESP8266 АТ командами. Хочу таку ж бібліотеку на Сі для STM32. Такого досвіду ще на маю.

І наче потроху виходить, але застряг на одній функції де йде активна робота з рядками. Може хто спєц в цьому і може допомогти мені?

Ось початковий код функції де застряг:

uint32_t ESP8266::checkIPD(String& data)
{
    //Serial.print("### check: ");
    //Serial.println(data);

    int32_t index_PIPDcomma = -1;
    int32_t index_colon = -1; /* : */
    int32_t index_comma = -1; /* , */
    int32_t len = -1;
    int8_t id = -1;
    { // Just for easier diffing
        index_PIPDcomma = data.indexOf("+IPD,");
        if (index_PIPDcomma != -1) {
            index_colon = data.indexOf(':', index_PIPDcomma + 5);
            if (index_colon != -1) {
                index_comma = data.indexOf(',', index_PIPDcomma + 5);
                /* +IPD,id,len:data */
                if (index_comma != -1 && index_comma < index_colon) { 
                    id = data.substring(index_PIPDcomma + 5, index_comma).toInt();
                    if (id < 0 || id > 4) {
                        return 0;
                    }
                    len = data.substring(index_comma + 1, index_colon).toInt();
                    if (len <= 0) {
                        return 0;
                    }
                } else { /* +IPD,len:data */
                    len = data.substring(index_PIPDcomma + 5, index_colon).toInt();
                    if (len <= 0) {
                        return 0;
                    }
                }
                if (m_onData) {
                    m_onData(id, len, m_onDataPtr);
                }
                return len;
            }
        }
    }
    return 0;
}

А ось те що переробив в функції з тими остатками, що не знаю як переробити. Затик саме на data.indexOf :

uint32_t ESP8266_checkIPD(uint8_t data[])
{
    //Serial.print("### check: ");
    //Serial.println(data);

    int32_t index_PIPDcomma = -1;
    int32_t index_colon = -1; /* : */
    int32_t index_comma = -1; /* , */
    int32_t len = -1;
    int8_t id = -1;
    { // Just for easier diffing
        index_PIPDcomma = strstr(data, "+IPD,");
        if (index_PIPDcomma != -1) 
        {
            // оце далі не знаю як переробити
            index_colon = data.indexOf(':', index_PIPDcomma + 5);
            if (index_colon != -1) 
            {
                index_comma = data.indexOf(',', index_PIPDcomma + 5);
                /* +IPD,id,len:data */
                if (index_comma != -1 && index_comma < index_colon) {
                    id = data.substring(index_PIPDcomma + 5, index_comma).toInt();
                    if (id < 0 || id > 4) {
                        return 0;
                    }
                    len = data.substring(index_comma + 1, index_colon).toInt();
                    if (len <= 0) {
                        return 0;
                    }
                } else { /* +IPD,len:data */
                    len = data.substring(index_PIPDcomma + 5, index_colon).toInt();
                    if (len <= 0) {
                        return 0;
                    }
                }
                if (m_onData) {
                    m_onData(id, len, m_onDataPtr);
                }
                return len;
            }
        }
    }
    return 0;
}
Подякували: NaharD1