STM32 实时时钟(RTC)的配置和时钟切换

    目前硬件核心板存在一个问题,外部32.768KHz的晶振经常无法起振,硬件工程师将晶振两端的1MΩ电阻去掉,电容从10pF换成6pF后大部分能起振了,个别的还是不行。目前是外部LSE无法起振的情况下直接死机,为了让上位机知道核心板是否在正常工作,要保证外部晶振无法起振的情况下切换到内部晶振,系统其他功能还能正常工作。

    先配置下外部时钟:

1、RCC选择外部晶振

2、Timers的RTC栏里,勾选 Activate两个,RTC OUT 选择 Disable

3、参数设置,日期格式采用二进制方式:Binary data format,其他日历时间自行设置

4、时钟树配置:选择外部晶振:LSE

    通过以上操作就完成了RTC的配置。

    在modbus校时指令和应用程序中对时间和日期进行更新,两个应用中都是对时间和日期的写操作,因此需要一个互斥量保持数据完整性。

    modbus校时线程代码如下:

    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};

    ......

    extern rt_mutex_t set_time_mutex;

    rt_mutex_take(set_time_mutex, RT_WAITING_FOREVER);

    ......
   
            case 0x08: // setyear  // 判断len, 如果len大于
                       break;
            case 0x09: // setmonth
                       break;
            case 0x0A: // setday
                       break;
            case 0x0B: // sethour
                       break;
            case 0x0C: // setminute
                       break;
            case 0x0D: // setsecond
                       break;
            case 0x0E: // setmsecond

                       sTime.Hours = armsyspara.settime.sethour;
                       sTime.Minutes = armsyspara.settime.setminu;
                       sTime.Seconds = armsyspara.settime.setsec;

                       if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
                       {
//                           Error_Handler();   // add alrm word
                       }
                       //DateToUpdate.WeekDay = RTC_WEEKDAY_TUESDAY;

                       DateToUpdate.Year = armsyspara.settime.setyear;
                       DateToUpdate.Month = armsyspara.settime.setmonth;
                       DateToUpdate.Date = armsyspara.settime.setday;

                       if(HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
                       {
//                           Error_Handler();   // add alrm word
                       }

                       LOG_I("Set Time: %02d/%02d/%02d %02d:%02d:%02d",2000 + DateToUpdate.Year, DateToUpdate.Month, DateToUpdate.Date,
                             sTime.Hours, sTime.Minutes, sTime.Seconds); // Display date and time Format: yy/mm/dd hh:mm:ss

                       break;  // 这一块一起处理
    ......
    
    rt_mutex_release(set_time_mutex);

    时间更新线程如下:

    RTC_DateTypeDef GetData;  //获取日期结构体
    RTC_TimeTypeDef GetTime;  //获取时间结构体

    set_time_mutex = rt_mutex_create("set_time_mutex", RT_IPC_FLAG_FIFO); // create set time mutex

    while (1)
    {
        HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);      // Get the RTC current Time
        HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);      // Get the RTC current Date

        rt_mutex_take(set_time_mutex, RT_WAITING_FOREVER);     // refresh set time
        armsyspara.settime.setyear  = GetData.Year;
        armsyspara.settime.setmonth = GetData.Month;
        armsyspara.settime.setday   = GetData.Date;

        armsyspara.settime.sethour  = GetTime.Hours;
        armsyspara.settime.setminu  = GetTime.Minutes;
        armsyspara.settime.setsec   = GetTime.Seconds;
        rt_mutex_release(set_time_mutex);

        rt_thread_mdelay(1000);
    }

    解决最后的问题。仿真的时候可以看到,外部晶振没有起振的情况下死机而无法正常运行,为了解决此问题,要修改超时的库函数,并且切换到LSI。

    void SystemClock_Config(void)改为:

void SystemClock_Config(void)
{
  uint8_t LSE_Status = HAL_OK;

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
      if(HAL_RCC_OscConfig(&RCC_OscInitStruct) == HAL_TIMEOUT)
      {
          RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSI;
          RCC_OscInitStruct.HSEState = RCC_HSE_ON;
          RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;

          RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
          RCC_OscInitStruct.LSIState = RCC_LSI_ON;

          RCC_OscInitStruct.HSIState = RCC_HSI_ON;
          RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
          RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
          RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

          HAL_RCC_OscConfig(&RCC_OscInitStruct);

          LSE_Status = HAL_ERROR;
      }
      else
      {
          Error_Handler();
      }
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC;

  if(HAL_ERROR == LSE_Status)
  {
      PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; // switch to LSI
  }
  else
  {
      PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; // default to LSE
  }

  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

    库函数 HAL_RCC_OscConfig(RCC_OscInitTypeDef  *RCC_OscInitStruct) 也要做修改,修改如下:其中超时使用  RCC_Delay(1);   // 72MHz,因为使用sysclick修改的代码量会很大。

    /* Set the new LSE configuration -----------------------------------------*/
    __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState);
    /* Check the LSE State */
    if (RCC_OscInitStruct->LSEState != RCC_LSE_OFF)  // modify by anbin 9.14
    {
      rt_tick_t tick = 0;

      /* Wait till LSE is ready */
      while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET)
      {
          RCC_Delay(1);                       // 72MHz
          tick++;

          if (tick > RCC_LSE_TIMEOUT_VALUE/2) // time out, switch to LSI
          {
              return HAL_TIMEOUT;
          }
      }
    }

    修改的程序测试,上电等待5s左右程序开始正常运行,关闭了LSE,切换到了内部LSI,后期可以补充将RTC初始化失败的标记传到上位机。

作者:星元的天空

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 实时时钟(RTC)的配置和时钟切换

发表回复