Main Menu
Menu

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.

Show posts Menu

Messages - TaoTao

#9
Quote from: dec on September 11, 2018, 08:12:45 AM
Quote from: TaoTao on September 11, 2018, 02:38:22 AM
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ 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 ได้ชัดเจนกว่าแค่นั้นเอง
ขอบคุณคร๊าบบบ  8)
#10
Quote from: ch25 on September 13, 2018, 11:04:45 AM
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่
ผมIAR เดิมๆ ครับ ตัว LIMIT 8kB ;D
เท่าที่ใช้มา ก็เพียงพอครับ
เพราะว่าโคดจึ๋งนึง ไม่ใหญ่โตอะไร
.
ถ้า อันลิมิต ชีวิตเกินร้อย น่าใช้อีกตัว ก็ COSMIC C เลยครับ เสียตรงที่ มันต้อง mail ไปขอ License เค้าทุกปีอ่ะ
.
แนะเริ่มจาก IAR ครับ
เพราะ UI ทันสมัย + ใช้ง่าย + เข้าใจง่าย + ยืดหยุ่น
#11
Quote from: dec on September 09, 2018, 10:26:58 PM
เกิดจากใน 1 statement มีการ access ตัวแปร volatile มากกว่า 1 ตัวครับ

เดาว่าน่าจะเป็น CNTRH กับ CNTRL เพราะส่วนใหญ่ Register มักจะมีการนิยามให้เป็น volatile

การที่เรานิยามตัวแปรให้เป็น volatile คือการนิยามว่าตัวแปรตัวนั้นมีการเปลี่ยนแปลงได้ตลอดเวลา
คาดการณ์ไม่ได้ ดังนั้น compiler จึงไม่ optimize ตัวแปรที่นิยามว่าเป็น volatile เพื่อให้เวลา
cpu ทำการ access ตัวแปร volatile จะต้องไปอ่านค่าจาก memory จริง ๆ ของตัวแปร volatile ตัวนั้น

การ access ตัวแปร volatile มากกว่า 1 ตัว ใน 1 statement นั้นไม่ได้ผิด แต่จะทำให้เกิดการคาดเดาพฤติกรรม
ของโปรแกรมไม่ได้

เช่น statement นี้
tim1_rec_[0] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );

ใน statement นี้มีการอ่านค่าจาก CNTRH และ CNTRL พร้อมกัน แต่ในความเป็นจริงแล้ว
การทำงานของ cpu มันจะอ่านทีละตัว จะอ่านตัวไหนก่อนก็ขึ้นอยู่กับ compiler ที่ใช้ในการ compile และ
ช่วงเวลาที่ cpu อ่านค่าจาก volatile ตัวแรกอยู่ ค่าของ volatile ตัวที่ 2 อาจเปลี่ยนแปลงไปแล้ว

เรื่องนี้ไม่ได้เป็นเรื่องร้ายแรงถ้าเรารู้ตัวว่าเรากำลังทำอะไรอยู่ เช่นกรณีนี้ เรารู้ว่าเราตัวว่าเราอ่านค่า counter ของ timer
ซึ่งมีการเพิ่มขึ้นเป็นเชิงเส้นเสมอ แต่ compiler มันไม่รู้ด้วย IAR มันเลยเตือนไปตาม ISO/ANSI standard

แต่ถ้าจะต้องการเลี่ยงไม่ให้เกิดการ warning เราก็ต้องทำให้ใน 1 statement มีการ access ตัวแปร volatile ตัวเดียว

เช่น
tim1_rec_[0] = TIM1->CNTRH;
tim1_rec_[0] = (( tim1_rec_[0] << 8 ) | (TIM1->CNTRL));


หรือเอาตัวแปรธรรมดามาช่วย
uint8_t tmp1, tmp2;
tmp1 = TIM1->CNTRH;
tmp2 = TIM1->CNTRL;
tim1_rec_[0] = ((tmp1  << 8 ) | (tmp2);


หรือถ้าเรามันใจว่าโปรแกรมเราจะทำงานได้ถูกต้องแน่นอน เราสามารถไปปิดการ warning สำหรับ case นี้ได้
โดยคลิกขวาที่ Project เลือก Option ไปที่ C/C++ Compiler เลือกแทบ Diagnostics
แล้วในช่อง Suppress these diagnostics: ให้พิมพ์ Pa082 แล้ว OK
เท่านี้มันจะไม่ warning case Pa082 อีกแล้ว
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

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

ขอบคุณค๊าบบบบ  ;D  ;D  ;D
#12
คือ ผมอ่านค่า  CNT of TIM1  มาเก็บไว้ที่  tim1_rec_[..] เพื่อดู machine cycle :

"tim1_rec_[0] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
tim1_rec_[1] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
tim1_rec_[2] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
"

ปรากฎว่า มันขึ้น warning แบบนี้ตลอดเลย

"Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this  D:\Dropbox\__WP\_PJ_STM8S_IAR\Project\my_prj\stm8s_it.c 127"

โค้ดทุกอย่างก็ทำงานตามที่คิดไว้ ทั้งหมด
แต่ แอบโตะใจที่มันเตือนนี่แหล่ะครับ    ::)

มีทางทำให้มันไม่ warning ไหมครับ
แค่ส่องค่า เท่านั้นเอง ทำมาเป็นเตือน  ;D
#13
Quote from: dec on September 07, 2018, 02:08:15 PM
ไม่มีโดยตรงครับ

ในทาง software ภาษา C มีคล้าย ๆ กันคือตัวแปรที่มี const นำหน้า คือตัวแปรคงที่
ต้องมีการกำหนดค่าเริ่มต้นตั้งแต่ประกาศตัวแปร compiler จะห้ามไม่ให้เปลี่ยนค่า
ตัวแปรคงที่ขณะ runtime

การใช้ const กับตัวแปร global และ local มีลักษณะต่างกันนิดหน่อย

ถ้าใช้ const กับตัวแปร global ตัวแปรตัวนั้นจะถูกเก็บลง flash แทนที่จะเก็บไว้ใน ram
เพราะมันจะไม่มีการเปลี่ยนค่าอีก ตอน compile มีค่าเป็นอะไร ก็ต้องมีค่าเท่านั้นไปตลอด

ถ้าใช้ const กับตัวแปร local ตัวแปรตัวนั้นจะยังเก็บไว้ใน ram เพราะค่าเริ่มต้นของ
ตัวแปร local มันจะถูกกำหนดได้ในขณะ runtime เท่านั้น ยังไงก็ต้องเก็บไว้ใน ram
แต่ถึงอย่างนั้นเราก็เอา pointer มาล้วงข้างได้ โดยประกาศตัวแปร pointer ที่ไม่ได้เป็น const
แล้วเอาไปชี้ตัวแปร const local ตัวนั้น แล้วเปลี่ยนค่าผ่าน pointer เอา แบบนี้
void function(void)
{
  const int x = 100;
  int *ptr = (int *)&x;

  printf("X is %d.\r\n", x);
  *ptr = 101;
  printf("X is %d.\r\n", x);
}


ผลลัพธ์คือ
X is 100.
X is 101.



ในทาง hardware ก็มีวิธีอยู่ 2 วิธีคือ

1. ถ้า mcu ตัวนั้นโปรแกรม flash ตัวเองได้ ก็เอาค่านั้นๆ ที่ไม่ต้องการเปลี่ยนอีก
โปรแกรมลง flash memory ไปเลย แล้วเวลาใช้งานก็เอาตัวแปร pointer to const
ชี้ไปยัง address flash นั้นๆ

สมมุติผมเก็บ address 0x10000000 ไว้โปรแกรมข้อมูล ข้อมูลผมเป็น struct ชื่อ config
ผมก็ประกาศตัวแปร pointer to const ชี้ไป address 0x10000000
const struct config *config_storage = (const struct config*)0x10000000;

หลังจากนั้นก็ใช้ mcu โปรแกรม flash address 0x10000000 ใหม่โดยกรอกข้อมูลใส่ตัวแปร struct config
แล้วก็โปรแกรมมันลงไปทั้ง struct โดย cast type เป็น uint8_t* หรือ void* ก็แล้วแต่
ฟังก์ชั่นโปรแกรม flash มันจะต้องการ
struct config tmp;

//ใส่ข้อมูลที่จะเขียนในตัวแปร tmp

flash_program((uint8_t*)&tmp, sizeof(struct config));


2. คือ mcu ระดับ high end เช่น arm cortex m4, m7 มันจะมี Memory Protection Unit (MPU)
ใช้สำหรับป้องกันการเข้าถึง memory address ต่างๆ นั่นเอง มันสามารถกำหนดได้ว่าจาก address region ไหน
ถึงไหน ทำอะไรได้ และห้ามทำอะไรได้ เช่น เขียนได้อย่างเดียว, อ่านได้อย่างเดียว, ห้าม execute เป็นต้น

โอ้ววว พระเจ้ายอด มันจอร์จมาก 😨
ขอบคุณมากมาย ก๊าบบบบ
ตรงประเด็นที่ต้องการเลย 😊
#14
เช่น มีตัวแปรประเภท struct
ให้มันรับค่า ip จากนั้นห้ามเปลี่ยนค่าอีกเลย

คำสั่งแบบนี้ มีไหมครับ
#15
หลักๆ RTC ก็มีเท่านี้แหล่ะมั๊ง
คือ ตั้ง Times, Alarm และรอ Alarm ทั้งแบบ ธรรมดา และ Interrupt

ในที่นี้ สาธิต Alarm Interrupt แบบ Date Mask ครับ
ไม่ Mask ไม่ไหวครับ รอกันเป็นวันเลยยยย  ;D ;D ;D

ท้ายๆ คลิป มันวิ่งเข้า Interrupt ก่อน Alarm ที่ตั้งไว้ 1 วินาที (อ่านจาก Register)
ขี้เกียจหาสาเหตุล่ะครับ ง่วง ขอนอนก่อน  ;D

คิดว่า น่าจะพอทำให้ คนที่เพิ่งเริ่มใช้งาน ง่ายขึ้นน่ะครับ
อ่อ พอดี คอม ไม่มีไมล์นะครับ เลยพิมพ์เอา  :P
https://youtu.be/QZGfhKpoJFE
#16
Quote from: S.poolpong93 on March 21, 2018, 02:31:41 PM
Quote from: TaoTao on March 16, 2018, 04:41:52 PM
System Workbench อยากใช้งานเป็นจัง
ลองเล่นละ ลงและลบ.. ตั้งหลายรอบ Tool เยอะดีแท้ ตาลายเจงๆ
ไม่ไหว หรือเราคงแก่ละ  ::)

เคยได้ใช้ ไอ้ อะไรนะ coco cookcook ไรนี้แหล่ะ แต่ก็ ไม่เข้ามือ

สุดท้ายเลย สำหรับผม จบที่ CUBE + KEIL  ;D
ตัวฟรี.. ได้ที่ 32kB ครับ ถ้าโปรเจคไม่ใหญ่
และไม่ได้มีโคด เกี่ยวกับ String - ตัวอักษร อะไรมากมายน่าจะเพียงพอ

ลอง CUBE + KEIL ไหมครับ
ถ้าว่างเดี๋ยวอาจทำ คลิปสั้นๆ สอนเบื้องต้นให้
เบื้องต้นจริงๆ นะ เพราะเป็นแค่นี้ ไม่ได้เรียนมาทางนี้  ;D

อาจจะ 32F051 32F103C8 ไม่ก็ 32F030 คือบอร์ดที่มีในมือ
รับรอง ใช้งานมันได้ ภายใน 10 นาที

ส่วนสำคัญสุด ที่ต้องเข้าใจผมว่า คือคำสั่ง "ระดับบิต" คือ & กับ |
และ C-Language ครับ ถ้าได้ก็ง่ายแล้วขึ้นแหล่ะ
อย่าง C สมัยผม ไม่มีเรียน เรียนมาแต่ Pascal

เช่นว่า ต้องการ ไอ้บิตตัวนี้เป็น 0 หรือ เป็น 1 ต้องทำไง ประมาณนี้
อีกหัวข้อ อันนี้ ต้องรู้ มันคือพระเอกเลย.. การ Interrupt ครับ ได้ใช้แน่นอน

ทีนี้ อยากใช้ Function อะไร เปิด DataSheet + RefMan
และอาจพ่วงด้วย Errata เพราะบางที Product เค้าเอง ก็มีจุดอ่อน-บกพร่อง ด้านนั้นๆ
แล้ว พิมพ์ตามมันเลย นี่คือวิธีที่ผมใช้นะ

เพราะ CUBE เอง ไม่สมบูรณ์ทุก Feature นะครับ
แต่ช่วย Initial ได้ดีมาก ซึ่งมาครึ่งทางแล้วแหล่ะ

ที่เหลือ เราอาจต้อง สร้างเอง.. ด้วยแรงตัณหา ครับ  ;D

รอชมคลิปครับ
ผิดจากที่เคยบอกไว้ ว่า 10 นาที เพราะคอมผม ไม่มีไมล์ เลยพิมพ์เอา
ขี้เกียจหาสาเหตุ ตอนท้ายๆ คลิปครับ
อยากรู้ว่า สาเหตุอะไร ลองชมครับ  O_o!
https://youtu.be/QZGfhKpoJFE