STM32F103 Clock RCC

Started by tha, September 14, 2020, 08:07:36 AM

Previous topic - Next topic

tha

วันนี้มาว่าเรื่อง clock ของ STM32F103 สักหน่อย ลองตั้งให้ใช้ clock จาก 8MHz crystal ภายนอกดู ก็ทำได้นะ ผมเซ็ตอย่างนี้



ลองเอาโปรแกรมย่อยนี้ไปใส่แทนในโปรเจ็คได้เลยครับ

Quote
   /**
     * @brief  System Clock Configuration
     *         The system Clock is configured as follow :
     *            System Clock source            = PLL (HSE)
     *            SYSCLK(Hz)                     = 64000000
     *            HCLK(Hz)                       = 64000000
     *            AHB Prescaler                  = 1
     *            APB1 Prescaler                 = 2
     *            APB2 Prescaler                 = 1
     *            PLLMUL                         = 8
     *            Flash Latency(WS)              = 2
     * @param  None
     * @retval None
     */
void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef clkinitstruct = {0};
  RCC_OscInitTypeDef oscinitstruct = {0};

  /* Configure PLL ------------------------------------------------------*/
  /* PLL configuration: PLLCLK = (HSE) * PLLMUL = (8 ) * 8 = 64 MHz */
  /* PREDIV1 configuration: PREDIV1CLK = PLLCLK / HSEPredivValue = 64 / 1 = 64 MHz */
  /* Enable HSI and activate PLL with HSi_DIV2 as source */
  oscinitstruct.OscillatorType  = RCC_OSCILLATORTYPE_HSE;
  oscinitstruct.HSEState        = RCC_HSE_ON;
  oscinitstruct.LSEState        = RCC_LSE_OFF;
  oscinitstruct.HSIState        = RCC_HSI_OFF;
  oscinitstruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  oscinitstruct.HSEPredivValue  = RCC_HSE_PREDIV_DIV1;
  oscinitstruct.PLL.PLLState    = RCC_PLL_ON;
  oscinitstruct.PLL.PLLSource   = RCC_PLLSOURCE_HSE;
  oscinitstruct.PLL.PLLMUL      = RCC_PLL_MUL8;
  if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
  {
    /* Initialization Error */
    while(1);
  }

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
     clocks dividers */
  clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
  clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
  {
    /* Initialization Error */
    while(1);
  }
}

เดี๋ยวจะลองไล่ทีละสเต็บดูกันนะครับ เช็คก่อนสักครู่ เอาที่เราใช้ก่อนนะ ทั้งหมดเลยเดี๋ยวจะสับสน มันเยอะมาก

tha

ก็มาดูแต่ละจุดกันนะ ว่ามันคืออะไรกันบ้าง
จุดแรกก็เลือก OSCILLATORTYPE เราก็เลือกเป็น crytal 8MHz ภายนอก ดูที่บิต register ก็ไม่มีบิตนี้อยู่นะ ก็ละเป็นที่เข้าใจก่อน มากมายเหลือเกิน หาไม่ไหว ท่านใดทราบก็อธิบายเสริมมานะครับ
Quote
oscinitstruct.OscillatorType  = RCC_OSCILLATORTYPE_HSE;

จุดนี้เราก็เลือก RCC_HSE_ON ส่วนตัวอื่นเราก็ OFF ไป ตัวนี้มีบิตให้เลือกเป็น '0' เป็น '1' ที่ Clock control register (RCC_CR)
Quote
oscinitstruct.HSEState        = RCC_HSE_ON;
oscinitstruct.LSEState        = RCC_LSE_OFF;
oscinitstruct.HSIState        = RCC_HSI_OFF;

ตัวนี้เรามาตั้งเป็น HSE แล้วคงจะไม่ได้ใช้แล้วใช่ไหม ก็ใส่เอาไว้ก็ได้ หรือลองเอาออก ดูสิว่ายังทำงานได้ไหม
Quote
oscinitstruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

นี่ที่ block diagram ก็ไม่เห็นมี คงมีในเบอร์ใหญ่ conectivity line (stm32f107) ก็เลือกหารด้วย '1' เอาไว้ หรือจะเอาออกก็ได้
มันจะมีบิต PLLXTPRE เพื่อเลือกว่าจะเอา HSE เข้า PLL โดยตรง(PLLXTPRE =0) หรือจะหาร HSE ด้วย 2 ก่อน (PLLXTPRE = 1) ก็ไม่มีให้ตั้งนะ ถ้าไม่มีให้ตั้งก็เป็น default คือ บิต PLLXTPRE จะเท่ากับ '0' คือเลือก HSE โดยตรง
Quote
oscinitstruct.HSEPredivValue  = RCC_HSE_PREDIV_DIV1;

จุดนี้เราก็เลือก PLL ON, เลือก HSE เป็น source ของ PLL, เลือก PLL คุณด้วย 8 ก็จะได้ clock เป็น 8x8 = 64MHz ถ้าเราตั้งเป็น MUL9 ก็จะได้ clock เป็น 8x9 = 72MHz ซึ่งเป็นค่าที่สูงสุดแล้ว ห้ามตั้งค่าเกินนี้ ถ้า crytal ของเราเท่ากับ 8MHz ลองตั้งตัวคูณ(MUL) ให้ต่ำลงมาก็ได้ ลงมาได้ถึง MUL2 เลย (ดูที่ Clock configuration register (RCC_CFGR)) ตามความต้องการใช้งานของเรา เราจะเปลี่ยน crytal ภายนอกเป็นค่าความถี่เท่าไหร่ก็ได้ ให้ตรงกับค่าการใช้งานของเราได้เลย ไม่ได้ฟิคอยู่ที่ 8MHz crytal
Quote
oscinitstruct.PLL.PLLState    = RCC_PLL_ON;
oscinitstruct.PLL.PLLSource   = RCC_PLLSOURCE_HSE;
oscinitstruct.PLL.PLLMUL      = RCC_PLL_MUL8;

tha

อันนี้คงจะเป็น library ของมันว่าเรา จะใช้ clock ไปทางไหนบ้าง ไม่มีบิต register ให้เลือก
Quote
clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);

เลือก system cock มาจาก PLL clock เลือกมาจากบิตไหนหว่า? ท่านใดทราบช่วยเสริมมานะครับ
Quote
clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

เลือกตัวหาร AHB clock ก็หาร system cock ด้วย '1' ก็เท่ากับ 64MHz
Quote
clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

เลือกตัวหาร APB2 clock ก็หาร AHB cock ด้วย '1' ก็เท่ากับ 64MHz
Quote
clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;

เลือกตัวหาร APB1 clock ก็หาร AHB cock ด้วย '2' ก็เท่ากับ 32MHz คือ APB1 clock นี้มีค่าสูงสุดไม่เกิน 36MHz
Quote
clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;

FLASH LATENCY = 2 ผมก็ไม่ทราบนะ ท่านใดทราบช่วยเสริมมาด้วยครับ
Quote
if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)


tha

ผมลองโปรแกรม Blinky เอาสองตัวนี้ออกก็ยังทำงานได้อยู่ ใช้ HSE นะครับ
Quote
//oscinitstruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;                   
//oscinitstruct.HSEPredivValue  = RCC_HSE_PREDIV_DIV1;

เอา RCC_PLL_MUL8 แก้มาเป็น RCC_PLL_MUL2 หลอด LED ก็กระพริบในเวลาเท่าเดิม แสดงว่าโปรแกรมย่อย HAL_Delay(); เขาคงทำมาได้ยืดหยุ่นดี
Quote
HAL_Delay(500);

tha

แต่พอเอาฟังชั่นย่อยอย่างนี้มาใช้กับโปรแกรม Blinky การตั้ง RCC_PLL_MUL8 กับตั้ง RCC_PLL_MUL2 จะเห็นผลแตกต่างกัน การตั้ง RCC_PLL_MUL2 ไฟกระพริบจะช้ากว่าการตั้ง RCC_PLL_MUL8 มาก แสดงว่ามันมีผล เราจะตั้ง MUL หรือเปลี่ยน crytal ให้เหมาะสมกับโหลดของเรายังไงก็ได้ ขอให้ clock มันไม่เกิน 72MHz
Quote
//------------------------------------------------------------------------------------------------//
//---------------------------------- Function delay ----------------------------------------------//
//------------------------------------------------------------------------------------------------//
void delay_ms(volatile unsigned long ms)  // delay 1 ms per count @ Crystal 8.0 MHz and PLL9x or SYSCLK = 72 MHz
{
        volatile unsigned long i,j;
   for (i = 0; i < ms; i++ )
   for (j = 0; j < 5525; j++ );
}
      

Quote
delay_ms(500);

dec

Quote from: tha on September 15, 2020, 08:50:49 AM
ก็มาดูแต่ละจุดกันนะ ว่ามันคืออะไรกันบ้าง
จุดแรกก็เลือก OSCILLATORTYPE เราก็เลือกเป็น crytal 8MHz ภายนอก ดูที่บิต register ก็ไม่มีบิตนี้อยู่นะ ก็ละเป็นที่เข้าใจก่อน มากมายเหลือเกิน หาไม่ไหว ท่านใดทราบก็อธิบายเสริมมานะครับ
Quote
oscinitstruct.OscillatorType  = RCC_OSCILLATORTYPE_HSE;

Quote from: tha on September 15, 2020, 09:44:36 AM
อันนี้คงจะเป็น library ของมันว่าเรา จะใช้ clock ไปทางไหนบ้าง ไม่มีบิต register ให้เลือก
Quote
clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);

อันนี้เข้าใจถูกแล้วครับ มันแค่ให้เราเลือกว่าเราจะ config อะไรบ้าง เพราะ struct มันมี config หลายอย่าง
แล้วบางอย่างค่า 0 มันก็มีความหมาย เพื่อไม่ให้สบสันว่าเราต้องการ set ค่า 0 หรือว่าเราไม่ได้ต้องการ config อะไร


Quote from: tha on September 15, 2020, 09:44:36 AM
FLASH LATENCY = 2 ผมก็ไม่ทราบนะ ท่านใดทราบช่วยเสริมมาด้วยครับ
Quote
if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)

Flash latency คือ ช่วงเวลาในการรอ output จาก Flash memory ให้เสถียรครับ ผมเคยอธิบายเรื่องนี้ไปในกระทู้นี้
https://www.electoday.com/index.php/topic,10210.msg40608.html#msg40608


Quote from: tha on September 15, 2020, 10:45:25 AM
เอา RCC_PLL_MUL8 แก้มาเป็น RCC_PLL_MUL2 หลอด LED ก็กระพริบในเวลาเท่าเดิม แสดงว่าโปรแกรมย่อย HAL_Delay(); เขาคงทำมาได้ยืดหยุ่นดี
Quote
HAL_Delay(500);

HAL_Delay ใช้ Interrupt SysTick ในการนับเวลาครับ มันเอาค่า System Clock มาคำนวณค่า Reload Value
เพื่อให้ได้คาบเวลาที่ 1ms เสมอครับ มันอยู่ในฟังก์ชั่น HAL_Init(); ลองเข้าไปดูได้ครับ

tha

ขอบคุณครับ คุณ dec เมื่อวานนั่งสมาธิ เครียดๆหน่อยเลยอ่านไม่ลง เช้ามาค่อยปลอดโปร่ง เลยอ่านได้
ต่อไปให้ลงพื้นฐานของ STM32F1 ใช่ไหม เดี๋ยวจะศึกษาดูก่อน คุณ dec มีอะไรเสริมก็เสริมมาได้นะครับ ช่วยกัน ผมก็ชักจะลืม