Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - dec

Pages: 1 [2] 3 4 ... 10
9
มี JLink อยู่แล้ว ลองเข้า Debug Mode ดูเลยครับว่ามันทำงานรึเปล่า
https://docs.platformio.org/en/latest/tutorials/nordicnrf52/arduino_debugging_unit_testing.html#tutorial-nordicnrf52-arduino-debugging-unit-testing

10
มีข้อสงสัยเกี่ยวกับ GPIOx->OSPEEDR  อยากสอบถามครับ
 คือ OSPEED มีความเกี่ยวข้องกับ System Clock config หรือไม่ครับ ถ้าผมใช้ board stm32d401re nucleo ซึ่ง maximum clock คือ 84MHz ถ้าเลือกใช้ OSPEED = 100MHz จะมีปัญหาไหมครับ

ไม่ได้เกี่ยวข้องกันครับ OSPEEDR จริงๆ แล้วใช้ในการปรับ Slew rate หรือก็คือความเร็วในการเปลี่ยนจาก High ไป Low (tf) หรือ Low ไป High (tr) ของ pin
การเปลี่ยนค่า OSPEEDR มันจะไปปรับเปลี่ยนวงจร Drive ของ Pin โดยการเพิ่ม Speed ก็เป็นการเพิ่มกระแสให้กับวงจร Driver เพื่อให้สัญญาณมันเปลี่ยนแปลงได้เร็วขึ้น
แต่ก็แลกมากับ noise ที่มากขึ้นไปด้วย แต่ถ้าเราปรับ Speed ต่ำ แต่เอา Pin ไปขับสัญญาณถี่มากๆ สัญญาณมันจะเปลี่ยนแปลงไม่ทันนั่นเอง

เลข MHz ที่ระบุมาเป็นความถี่ที่ ST ได้จากการทดสอบ Pin โดยเอา Pin มาต่อ Load ค่าหนึ่งแล้ว Toggle High Low ในความถี่ค่าหนึ่ง แล้วก็วัดเวลา tf + tr เทียบกับคาบของสัญญาณ (T)
ซึ่งความถี่ที่เหมาะสมคือ tf + tr ต้องไม่เกิน (2/3)T และชิปแต่ละ Series ก็จะแตกต่างกันไป ซึ่งดูได้จาก Datasheet แต่เวลาใช้งานจริงก็อาจไม่ได้ผลตามที่ระบุ
เพราะมันมีปัจจัยอื่นเข้ามาเสริมด้วยเช่น ขนาดของ Load ที่ pin ขับอยู่ กับ ระดับแรงดันที่ใช้ เป็นต้น แต่ก็ใช้พอจะใช้เป็นค่าอ้างอิงได้ครับ

11
  ผมพึ่งจะเริ่มต้น STM32 ครับ ตอนนี้ซื้อ Board STM32F401RE nucleo มาศึกษา
  พอดีสงสัยครับ  ทำไมต้องมีการ set ค่า FLASH->ACR     =    0x00000605;        // จัดเวลา Flash ROM is 5 Wait state
  ถ้าไม่มีคำสั่งนี้จะเกิดอะไรขึ้น
  ไม่แน่ใจจะมีคนตอบรึเปล่าเพราะ กระทู้ตั้งแต่ปี 2013
   
  ขอบคุณครับ

โดยปกติวงจร logic เมื่อเราป้อน input ไป วงจรต้องใช้เวลาเล็กน้อยในการส่งออก output ครับ
ซึ่ง flash memory ก็เป็นลักษณะเดียวกัน แต่ flash memory จะใช้เวลาที่ค่อนข้างนานครับ

เวลา controller จะ อ่านคำสั่งหรือข้อมูลจาก flash memory นั้น controller ก็จะส่ง address ของ
ข้อมูลที่ต้องการอ่านไปให้ flash ทาง address bus พอ flash ได้รับ address แล้ว flash จะส่งข้อมูล
กลับมาให้ทาง data bus ครับ แต่สัญญาณที่ flash ส่งกลับมา มันจะยังไม่เสถียรในทันที controller ต้องรอ
ไปซักระยะหนึ่งให้สัญญาณที่ flash ส่งกลับมาเสถียรก่อน แล้วจึงอ่านข้อมูลจาก data bus ครับ

และหน่วยของเวลาที่ controller มันจะจับเวลาได้มันก็มีแค่จำนวน pulse ของสัญญาณ clock นั้นแหละครับ
5 Wait state ก็หมายถึงรอ 5 Clock Tick ก่อนที่จะอ่านข้อมูลกลับมา

ระยะเวลา Wait state นี้มีหลายปัจจัยครับ หลักๆ 3 ปัจจัยก็คือ

1. เทคโนโลยีของ flash ถ้า flash memory ดีๆ ก็จะส่งสัญญาณให้เสถียรได้เร็ว
    เช่น flash memory ใน STM32H7 ครับ ความถี่ clock สูงถึง 480MHz แต่รอแค่ 2 wait state

2. ความถี่ clock ที่ใช้ในการสื่อสารกับ flash ซึ่งปกติช่วงเวลาที่รอให้สัญญาณเสถียรมันจะคงที่
    แต่ถ้าเราปรับความถี่ clock สูงขึ้น คาบเวลาในแต่ละ clock ก็จะน้อยลง ก็ต้องเพิ่มจำนวน wait state clock

        เช่น flash ต้องใช้เวลา 100ns เพื่อให้สัญญาณเสถียร ถ้าใช้ความถี่ clock ที่ 50MHz จะมีคาบเวลาอยู่ที่ 20ns ต่อ 1 clock
        ก็ต้องรออย่างน้อย 5 wait state ขึ้นไปถึงจะเพียงพอให้สัญญาณจาก flash เสถียร (5*20ns = 100ns)

        แต่ถ้าเราเพิ่มความถี่ clock ไปที่ 100MHz แทน คาบเวลาอยู่ที่ 10ns ก็ต้องรออย่างน้อย 10 wait state ขึ้นไป
        ถึงจะเพียงพอให้สัญญาณจาก flash เสถียร (10*10ns = 100ns)

3. ระดับแรงดันที่ใช้ในระบบ ถ้าแรงดันยิ่งสูงสัญญาณก็จะเสถียรเร็วขึ้นครับ

        ยกตัวอย่าง STM32F401RE ใน Datasheet หน้า 61 ตารางที่ 15
               ถ้าใช้ VDD = 1.8V และความถี่ Clock = 84MHz ต้องรอ 4 wait states ขึ้นไป
               ถ้าใช้ VDD = 3.3V และความถี่ Clock = 84MHz ต้องรอ 2 wait states ขึ้นไป


มาถึงตรงนี้ ก็น่าจะพอเดาได้แล้วว่า ถ้าไม่มีการ set ค่า wait state ให้ถูกต้องจะเกิดผลอะไรหรือไม่

ปกติแล้ว ถ้าผมจำไม่ผิด STM32 แทบทุกตัว จะเริ่มการทำงานด้วยการใช้แหล่งกำเนิดสัญญาณ Clock ภายใน
หรือที่เรียกว่า HSI ซึ่งจะมีความถี่อยู่ที่ 16MHz เสมอ และจะตั้ง wait states ไว้ที่ 0

เวลาที่เราจะทำการ Config Clock เพื่อเปลี่ยนแหล่งกำเนิดสัญญาณ Clock ใหม่ ให้สูงขึ้น เราต้องพิจารณาว่า
ความถี่ใหม่ที่จะใช้นี้ต้องเพิ่ม wait states รึเปล่า ถ้าต้องเพิ่ม wait states  เราต้องทำการปรับ wait states
ก่อนที่จะทำการเปลี่ยนแหล่งกำเนิดสัญญาณ Clock ของระบบ
ไม่เช่นนั้นทันทีที่เปลี่ยนแหล่งกำเนิดสัญญาณ Clock
คำสั่งต่อไปที่ controller ทำการ fetch มาจะผิดไปหมด แล้วก็จะไปจบที่ Hardfault

ถ้าปรับ wait states สูงเกินไป controller ก็ยังทำงานได้ปกติครับ แต่ controller จะเสียเวลาในการรอข้อมูลไปเปล่าๆ
ซึ่งดูเหมือนว่ามันจะน้อย แต่ controller มีการ fetch คำสั่งตลอดเวลา ใน 1 วินาที fetch เป็นล้านครั้ง เมื่อรวมเวลาที่เสียไปมันก็มากอยู่ครับ

12
การสร้าง sine wave ความถี่สูง ประมาณ 100-500kHz ด้วย PWM
ค่อนข้างจะเกินความสามารถของ Microcontroller

-------------------------------------------------------------------------------------------------------------

อธิบายพื้นฐานของ Timer กับ PWM ของ STM32 คร่าว ๆ ก่อน ถ้าเข้าใจอยู่แล้วข้ามไปได้เลย

Timer แต่ละตัวของ STM32 จะมีความถี่สัญญาณ Timer Clock ไม่เท่ากัน
ขึ้นอยู่กับว่า Timer ตัวนั้นเกาะอยู่บน Bus APB1 หรือ APB2

อ้างอิงตาม STM32F407

Timer ทุกตัวจะมี Phase Lock Loop เพิ่มความถี่สัญญาณ Timer Clock เป็น 2 เท่า
ของสัญญาณ Clock จาก Bus APBx

ซึ่ง STM32F407 มีความถี่ Clock APB1 อยู่ที่ 42MHz และ APB2 จะอยู่ที่ 84MHz
ดังนั้นความถี่ Timer Clock ที่อยู่บน APB1 จะเป็น 84MHz และบน APB2 จะเป็น 164MHz





การทำงานก็ง่ายๆ วงจร Timer ก็จะป้อน Timer Clock ให้กับ Counter Register เพื่อ Trigger
ให้นับเลข ถ้าความถี่ Timer Clock สูงเกินไป STM32 ยังมีขั้นตอนให้สามารถ Scale
ลดความถี่ Timer Clock ได้ โดยกำหนดค่า Prescaler ซึ่งมีสูตรคำนวณเป็น

Timer Clock = Max Timer Clock / ( Prescaler + 1 )

Timer ส่วนใหญ่จะมี Counter Register ขนาด 16Bit บางตัวก็มี 32Bit
Timer จะนับเลขไปเรื่อยๆ จนเกิดการ Overflow แล้วก็จะกลับมาที่ 0
ณ เวลาที่ Counter Register เกิดการ Overflow จะเรียก Event นี้ว่า "Update"
ซึ่งเป็น 1 เหตุการณ์ที่สามารถโปรแกรมให้เกิดการ Interrupt ได้

ต่อไป การรับจนเกิด Overflow นั้น Counter Register ขนาด 16Bit ต้องนับเลขถึง 65535
ถึงจะเกิดการ Overflow มันอาจจะนานเกินไปที่จะให้นับถึง 65535 ซึ่งเราสามารถกำหนดค่า
ที่จะเกิดการ Overflow ได้โดยกำหนดค่า Period เช่นถ้าเรากำหนด Period = 1000
Timer จะนับมาถึง 1000 แล้วต่อไปก็จะกลายเป็น 0 เลย


ทีนี้จะทำ PWM แบบ Hardware หลักการทำงานก็ Timer บางตัวของ STM32
จะมี Output Compare Channel ซึ่งจะผูกกับขาของ STM32 เลย (ต้องเปิด Datasheet เพื่อดูว่า
Timer แต่ละตัวมี Output Compare Channel ออกที่ขาไหนบ้าง มันจะเขียนประมาณว่า TIM1_CH1, TIM4_CH2)
โดยตัว Output Channel นี่จะมีการให้เซ็ตค่า Register ค่าหนึ่ง เรียกว่าค่า Compare
ถ้า Counter Register นับจนเกินค่า Compare สัญญาณที่ขาของ STM32 ที่ใช้เป็น Output Compare Channel
จะเกิดการ Toggle แล้วเมื่อ Counter เกิดการ Overflow ก็จะ Toggle กลับไปยัง State ปกติ

ยกตัวอย่าง ตั้งค่า Period ไว้ที่ 100 เพื่อต้องการให้ปรับ Duty cycle ได้ 101 ค่า (0 - 100)
ตั้งค่า Compare ไว้ที่ 50 ระหว่างที่ Counter Register นับ 0 - 50 สัญญาณที่ขาจะเป็น High
พอนับเลยไป 51 สัญญาณที่ขาจะสลับเป็น Low ทันที แล้วเมื่อ Counter Register เกิด Overflow
สัญญาณที่ขาจะกลับเป็น High

-------------------------------------------------------------------------------------------------------------

ทีนี้มาลองคำนวณง่ายๆ

Sine Wave 500kHz มีความถี่ Nyquist อยู่ที่ 1MHz
การแปลง Sine Wave จาก Digital มาเป็น Analog
เราต้องการ Sampling Rate อย่างต่ำที่ 2 เท่าของความถี่ Nyquist (4 เท่าของ Sine Wave)
ก็คือ 2MHz

แปลว่าเราต้องสร้างสัญญาณ PWM ที่มีความถี่อย่างน้อย 2MHz

เราจะได้สัญญาณ PWM ที่ใช้นิยามสัญญาณ Sine Wave 500kHz หน้าตาประมาณนี้ (กราฟสีเขียว)



แล้วถ้าห่างต้องการให้ปรับ Duty cycle ได้ 101 ระดับ (0 - 100) เราต้องกำหนดค่า Period = 100

เมื่อได้เงื่อนไขแล้ว ลองใช้ Timer 1 เป็นตัวอ้างอิงในการคำนวณดู

Timer 1 มี Max Timer Clock อยู่ที่ 168MHz
กำหนด Prescaler เป็น 0 เลย

Timer Clock = 168MHz / ( 0 + 1 ) = 168MHz

กำหนด Period = 100 Timer ต้องนับ 100 + 1 ครั้ง เพื่อให้เกิด Overflow

ความถี่ PWM = 168MHz / (100 + 1) = 1.6634MHz

จะเห็นว่าไม่สามารถทำสัญญาณ PWM ที่มีความถี่อย่างน้อย 2MHz ได้
อาจต้อง Upgrade  ไปใช้ STM32 ที่แรงขึ้นกว่านี้อย่าง STM32F7 หรือ STM32H7
ที่มีความเร็วสูงถึง 216MHz และ 400MHz ตามลำดับ ซึ่งมีราคาแพงกว่า

ถ้าสามารถ Upgrade ได้ ก็ต้องลองคิดดูว่าสัญญาณ PWM หน้าตาแบบนั้น
เพียงพอที่จะใช้งานรึเปล่า ต่อให้ใช้ STM32H7 เองก็อาจเพิ่ม Sampling Rate ได้อีกแค่ 1 เท่า
ปกติถ้าจะให้กราฟออกมาสวยๆ อาจต้องใช้ Sampling Rate ซัก 50 เท่าของ Sine Wave
หรือก็คือ 25MHz ทีนี้จะหา Microcontroller ตัวไหนที่มันจะสร้าง PWM 25MHz ได้กันล่ะ

-------------------------------------------------------------------------------------------------------------

ต่อมาเรื่อง Code

ในจุดนี้ผมคิดว่าคุณน่าจะรู้แล้วว่าจะเปลี่ยนค่า Duty Cycle ตอนไหน ก็ทุกๆ ครั้งที่เกิด Event Update หรือ
Timer Overflow นั่นเอง ผมจะอ้างอิงจาก Code ที่คุณยกมาเลยนะ ทำต่อจากนั้นเลย
ก่อนอื่นต้องเขียน Code เพิ่มเพื่อเปิดใช้งาน Timer Interrupt

ย่างแรกใส่ #include "misc.h" เพิ่ม (Library บางตัวเขียนเต็มๆ ว่า "stm32f4xx_misc.h")

ใน Function int main(void)
เพิ่ม NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); ไว้ก่อน TM_LEDS_Init();

ใน Function void TM_TIMER_Init(void)
ประกาศตัวแปร NVIC_InitTypeDef NVIC_InitStruct; เพิ่ม
แล้วลงมาก่อนบรรทัด TIM_Cmd(TIM4, ENABLE); เพิ่ม Code ดังนี้

Quote
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
   
    NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);

Code ส่วนที่เพิ่มมาคือ เปิดให้ Timer 4 ส่งสัญญาณ Event Update ไปให้ CPU
และทำการเปิดใช้งาน Interrupt Request สำหรับ Timer 4 (เปิดใช้งานให้ CPU กระโดดไปทำงาน
ที่ Function Interrupt ได้นั่นแหละ)

เพิ่ม Function TIM4_IRQHandler เพื่อให้ CPU กระโดดมาทำงาน
ตามที่ถามว่าจะเปลี่ยน Duty Cycle ยังไง จริงๆ เปลี่ยนได้ง่ายๆ
โดยใช้ Function TIM_SetCompareX โดย X คือเลข Channel

Code: [Select]
void TIM4_IRQHandler(void)
{
  uint32_t compare;
  static uint32_t up_down_flags = 0;
 
  if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) == SET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
   
    compare = TIM_GetCapture1(TIM4);
   
    if(up_down_flags == 0)
    {
      if(compare == 8399)
      {
        up_down_flags = 1;
        compare--;
      }
      else
        compare++;
    }
    else
    {
      if(compare == 0)
      {
        up_down_flags = 0;
        compare++;
      }
      else
        compare--;
    }
   
    TIM_SetCompare1(TIM4, compare);
  }
}

ใน Function interrupt นี้ผมเขียนให้มันเปลี่ยน Duty Cycle ขึ้นลงเฉยๆ คุณจะเห็นไฟสีเขียวหรี่แล้วสว่างขึ้น

อันนี้ Code เต็มๆ
Code: [Select]
/**
 *    PWM example for STM32F4 Discovery
 *    It should work on STM32F429 Discovery too and all other STM32F4xx devices
 *
 *    @author     Tilen Majerle
 *    @email        tilen@majerle.eu
 *    @website    http://stm32f4-discovery.net
 *    @ide        Keil uVision 5
 */
#include "stm32f4xx.h"
#include "misc.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_tim.h"
 
void TM_LEDS_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
   
    /* Clock for GPIOD */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
 
    /* Alternating functions for pins */
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4);
   
    /* Set pins */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStruct);
}
 
void TM_TIMER_Init(void) {
    NVIC_InitTypeDef NVIC_InitStruct;
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
   
    /* Enable clock for TIM4 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*   
    TIM4 is connected to APB1 bus, which has on F407 device 42MHz clock                 
    But, timer has internal PLL, which double this frequency for timer, up to 84MHz     
    Remember: Not each timer is connected to APB1, there are also timers connected     
    on APB2, which works at 84MHz by default, and internal PLL increase                 
    this to up to 168MHz                                                             
   
    Set timer prescaller
    Timer count frequency is set with
   
    timer_tick_frequency = Timer_default_frequency / (prescaller_set + 1)       
   
    In our case, we want a max frequency for timer, so we set prescaller to 0         
    And our timer will have tick frequency       
   
    timer_tick_frequency = 84000000 / (0 + 1) = 84000000
*/   
    TIM_BaseStruct.TIM_Prescaler = 0;
    /* Count up */
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*
    Set timer period when it have reset
    First you have to know max value for timer
    In our case it is 16bit = 65535
    To get your frequency for PWM, equation is simple
   
    PWM_frequency = timer_tick_frequency / (TIM_Period + 1)
   
    If you know your PWM frequency you want to have timer period set correct
   
    TIM_Period = timer_tick_frequency / PWM_frequency - 1
   
    In our case, for 10Khz PWM_frequency, set Period to
   
    TIM_Period = 84000000 / 10000 - 1 = 8399
   
    If you get TIM_Period larger than max timer value (in our case 65535),
    you have to choose larger prescaler and slow down timer tick frequency
*/
    TIM_BaseStruct.TIM_Period = 8399; /* 10kHz PWM */
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_RepetitionCounter = 0;
    /* Initialize TIM4 */
    TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);
   
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
   
    NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);
   
    /* Start count on TIM4 */
    TIM_Cmd(TIM4, ENABLE);
}

void TM_PWM_Init(void) {
    TIM_OCInitTypeDef TIM_OCStruct;
   
    /* Common settings */
   
    /* PWM mode 2 = Clear on compare match */
    /* PWM mode 1 = Set on compare match */
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
   
/*
    To get proper duty cycle, you have simple equation
   
    pulse_length = ((TIM_Period + 1) * DutyCycle) / 100 - 1
   
    where DutyCycle is in percent, between 0 and 100%
   
    25% duty cycle:     pulse_length = ((8399 + 1) * 25) / 100 - 1 = 2099
    50% duty cycle:     pulse_length = ((8399 + 1) * 50) / 100 - 1 = 4199
    75% duty cycle:     pulse_length = ((8399 + 1) * 75) / 100 - 1 = 6299
    100% duty cycle:    pulse_length = ((8399 + 1) * 100) / 100 - 1 = 8399
   
    Remember: if pulse_length is larger than TIM_Period, you will have output HIGH all the time
*/
    TIM_OCStruct.TIM_Pulse = 2099; /* 25% duty cycle */
    TIM_OC1Init(TIM4, &TIM_OCStruct);
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
   
    TIM_OCStruct.TIM_Pulse = 4199; /* 50% duty cycle */
    TIM_OC2Init(TIM4, &TIM_OCStruct);
    TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
   
    TIM_OCStruct.TIM_Pulse = 6299; /* 75% duty cycle */
    TIM_OC3Init(TIM4, &TIM_OCStruct);
    TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
   
    TIM_OCStruct.TIM_Pulse = 8399; /* 100% duty cycle */
    TIM_OC4Init(TIM4, &TIM_OCStruct);
    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
}
 
int main(void) {
    /* Initialize system */
    SystemInit();
   
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
 
    /* Init leds */
    TM_LEDS_Init();
    /* Init timer */
    TM_TIMER_Init();
    /* Init PWM */
    TM_PWM_Init();
   
    while (1) {
    }
}

void TIM4_IRQHandler(void)
{
  uint32_t compare;
  static uint32_t up_down_flags = 0;
 
  if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) == SET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
   
    compare = TIM_GetCapture1(TIM4);
   
    if(up_down_flags == 0)
    {
      if(compare == 8399)
      {
        up_down_flags = 1;
        compare--;
      }
      else
        compare++;
    }
    else
    {
      if(compare == 0)
      {
        up_down_flags = 0;
        compare++;
      }
      else
        compare--;
    }
   
    TIM_SetCompare1(TIM4, compare);
  }
}

-------------------------------------------------------------------------------------------------------------

สุดท้าย มีเรื่องที่อยากจะบอกคือ CooCox CoIDE หยุดพัฒนาแล้ว เป็นไปได้ก็อยากให้เปลี่ยนครับ
มีตัวที่คล้ายๆ กันอยู่เช่น System Workbench for STM32 และ TrueSTUDIO ทั้ง 2 ตัวนี้เป็น
Eclipse based เหมือนกับ CoIDE โดยเฉพาะ TrueSTUDIO ตอนนี้ ST เป็นเจ้าของเอง

และ Library ที่คุณใช้อยู่เรียกว่า Standard Peripheral Library (SPL) เป็น Library เก่าที่ ST
ไม่พัฒนาต่อแล้ว ST ผลักดันให้ใช้ STM32Cube แทน

ทั้งนี้ทั้งนั้นแล้ว ถ้ามันยังใช้พัฒนาได้ก็ขึ้นอยู่กับการตัดสินใจของคุณครับ ส่วนตัวผมก็ชอบ SPL
แต่ก็ต้องยอมไปใช้ STM32Cube เพราะชิปตัวใหม่ๆ ไม่มี SPL ให้ใช้แล้ว หรือถ้าสนใจจะเปลี่ยนไปใช้
Cube หรือเปลี่ยน IDE ก็ไม่ต้องรีบครับ ทำงานนี้ให้เสร็จก่อน แล้วค่อยไปหัดใช้ทีหลัง

-------------------------------------------------------------------------------------------------------------

สุดท้ายอีกที STM32 น่าจะอยู่หมวด ARM นะครับ

13
เคยใช้ใน stm32 ครับ ปกติพวกบอร์ด devkit stm32 รุ่นหลังๆ มันจะลด cost ไม่ได้ใส่ crystal มา
แต่มันจะให้ st-link ที่ built-in มาบนบอร์ดจ่าย clock มาให้แทน

ตรงนี้มันก็ขึ้นอยู่กับว่าเรามีแหล่งกำเนิดสัญญาณเป็นแบบไหนครับถ้าเป็น crystal ก็ใช้ HSE ปกติ
มันจะรับสัญญาณ sine wave จาก crystal มาทำ square wave ก่อนนำไปใช้

ถ้าแหล่งกำเนิดสัญญาณเป็น square wave มาแล้ว (จ่ายมาจาก mcu ตัวอื่น หรือใช้ oscillator)
ก็ใช้ HSE Bypass มันจะเอาสัญญาณที่เข้ามาไปใช้เลย

เท่าที่ได้ใช้มา มันก็ไม่ได้ต่างกันครับ ใช้ HSE Bypass + PLL ได้ปกติเหมือนใช้ HSE ปกติครับ
แต่ผมก็เคยใช้เฉพาะบนบอร์ด devkit เท่านั้นเองครับ เวลาทำบอร์ดเองก็ใส่ crystal ตลอด

14
สามารถโปรแกรมผ่าน ST Link ได้ครับ ใช้ ST Link กับโปรแกรม STM32 ST-LINK utility
โหลดจากที่นี่ https://www.st.com/en/development-tools/stsw-link004.html

15
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่

ผมใช้ IAR for ARM แบบ crack ครับ โหลดตัว Free-Trials จากเว็บ IAR ส่วนตัว crack โหลดจากกระทู้นี้ http://www.electoday.com/index.php/topic,15428.0.html

crack มันสามารถเลือก product เป็น iar for stm8 ได้ ลองดูนะครับ

16
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ volatile
ตอนนี้ หายแล้วๆ ผมทำแบบนี้อ่ะครับ
tim1_rec_[0]  = TIM1->CNTRH << 8;
tim1_rec_[0] |= TIM1->CNTRL;
อ่อ ต้องเก็บ High ก่อน ค่าจะได้ไม่ผิดเพี้ยนเยอะ  ;D

ขอบคุณค๊าบบบบ  ;D  ;D  ;D
จริงๆ มันก็ต้องซ้ายไปขวาแหละครับ อันนี้ผมสับสนเอง ต้องขอโทษด้วยครับ ยังงงตัวเองเหมือนกัน
ว่าทำไมถึงคิดออกมาได้ว่ามันอาจไม่ซ้ายไปขวา

จริงๆ ต้องบอกว่าใน 1 statement มันก็ต้องใช้คำสั่ง assembly มากกว่า 1 คำสั่ง
มันเสี่ยงที่จะเกิดเหตุการณ์ อ่านค่า volatile ตัวแรกมาแล้ว แล้วก่อนจะอ่านค่าอีกตัว มันเกิด interrupt
หรือเกิด context switch กรณีใช้ os ขึ้น แล้วทำให้ volatile อีกตัวเปลี่ยนค่าไปก่อนจะอ่านมันนั่นแหละครับ

เรื่องแบบนี้เกิดได้กับตัวแปรทุกตัว แต่ถ้านิยามเป็น volatile แล้ว compiler มันจะเอาใจใส่เป็นพิเศษกว่า

หลายคนยังเข้าใจผิดว่า 1 statement มันจะประมวลผลจนเสร็จ แล้วค่อยเกิด interrupt หรือ context switch
แต่ในความเป็นจริงมันเกิดในระดับภาษา assembly มันจึงมีความเสี่ยงที่ statement นั้น จะไม่ได้ผลลัพธ์ตามที่เขียนนั่นเอง

สุดท้ายแล้วถ้าแยกมาเขียน เป็นหลาย statement แทนมันจะต่างอะไร ก็ต้องบอกว่าไม่ต่างกันครับ แต่มันจะคาดเดาผลลัพธ์
ของแต่ละ statement ได้ชัดเจนกว่าแค่นั้นเอง

Pages: 1 [2] 3 4 ... 10