Tạo quả lắc (đồng hồ) theo chu kỳ 1 giây

Liên hệ QC

ndu96081631

Huyền thoại GPE
Thành viên BQT
Super Moderator
Tham gia
5/6/08
Bài viết
30,703
Được thích
53,952
Với sự trợ giúp của hàm API, tôi đã tạo ra được 1 cái đồng hồ quả lắc!
Code như sau:
1> Trong Module
PHP:
Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Public iCycle As Long, sHour As Long
PHP:
Sub StartTimer()
  StopTimer
  SetTimer Application.hWnd, 1, 25, AddressOf TimeProc
End Sub
PHP:
Sub StopTimer()
  KillTimer Application.hWnd, 1
End Sub
Mã:
Private Function TimeProc(ByVal H As Long, ByVal nMSG As Long, ByVal nID As Long, ByVal nTsys As Long)
  Dim sTotal As Double, hPos As Double, mPos As Double, sPos As Double
  On Error Resume Next
  sTotal = Hour(Time) * 3600 + Minute(Time) * 60 + Second(Time)
  sPos = sTotal * 6 Mod 360
  mPos = (sTotal / 10) Mod 360
  hPos = (sTotal / 120) Mod 360
  With Sheet1
    .Shapes("sec").Rotation = sPos
    .Shapes("min").Rotation = mPos
    .Shapes("hrs").Rotation = hPos
    [COLOR=Red][B].Shapes("Grp").Rotation = (Sin(WorksheetFunction.Radians(45) * (iCycle) / 4 + Atn(1) * 2)) * 20[/B][/COLOR]
    iCycle = iCycle + 1
  End With
  If sPos = 0 And mPos = 0 Then
    sHour = 1 * Left(Format(Time, "hh:mm:ss AM/PM"), 2)
    Call PlaySound.Main
  End If
End Function
2> Code cho CommandButton trên sheet
PHP:
Private Sub CommandButton1_Click()
  iCycle = 0
  With Sheet1.CommandButton1
    Run IIf(.Caption = "Start", "StartTimer", "StopTimer")
    .Caption = IIf(.Caption = "Start", "Stop", "Start")
  End With
End Sub
Code chạy ổn định nhưng còn 1 vấn đề nhỏ nhờ các cao thủ trợ giúp
Đoạn màu đỏ dùng để điều khiển quả lắc ---> Biết rằng đấy là hàm SIN theo thời gian nhưng tôi chẳng biết phải tinh chỉnh thế nào để bảo đảm quả lắc ấy hoạt động chính xác theo chu kỳ 1 giây ----> Ai có kinh nghiệm xin góp ý giúp tôi với (chỉ cái quả lắc thôi)
(hiện tại đang tạm chỉnh bằng thí nghiệm trực tiếp nhưng không chính xác lắm)
 

File đính kèm

Ôi! Đúng là mình ngu thật ---> Sao không nghĩ đến phương trình dao động của con lắc đơn nhỉ?
Mang nó vào đây là CHUẨN VÔ ĐỊCH luôn!
Cảm ơn các bạn đã quan tâm (điên đầu từ hôm qua đến giờ)
 
Upvote 0
Theo suy luận thì vị trí của quả lắc phải được tính toán dựa trên Time Mod 3600 thì mới chính xác.
 
Upvote 0
Theo suy luận thì vị trí của quả lắc phải được tính toán dựa trên Time Mod 3600 thì mới chính xác.
Vị trị quả lắc không quan trọng sư phụ à! Miễn sao nó hoạt động đúng chu kỳ 1s là được rồi (tức pha ban đầu = bao nhiêu cũng được)
Nói chung là em vừa tìm ra xong (khi vừa post bài) ---> Phương trình A*Sin(ωt + φ)
PHP:
.Shapes("Grp").Rotation = Sin(Atn(1) * (iCycle / 4 + 1 / 2)) * 20
(Hàm TimeProc ở trên hoạt động theo xung nhịp 25 ms ---> Tức nhịp 40 lần sẽ = 1 giây)
 
Lần chỉnh sửa cuối:
Upvote 0
Quá hay cảm ơn anh ndu
 
Upvote 0
Ăn tiền là
- Ngắn gọn
- Chạy mà không ảnh hưởng gì đến công việc khác
Đúng không?
Kết hợp với Module PlayMusicalNote, ta được thêm tính năng gõ boong boong khi đúng giờ
Ẹc... Ẹc...
(Bài này tôi có tham khảo ý kiến của bạn Nguyễn Duy Tuân)
 
Upvote 0
Vị trị quả lắc không quan trọng sư phụ à! Miễn sao nó hoạt động đúng chu kỳ 1s là được rồi (tức pha ban đầu = bao nhiêu cũng được)
Nói chung là em vừa tìm ra xong (khi vừa post bài) ---> Phương trình A*Sin(ωt + φ)
PHP:
.Shapes("Grp").Rotation = Sin(Atn(1) * (iCycle / 4 + 1 / 2)) * 20
(Hàm TimeProc ở trên hoạt động theo xung nhịp 25 ms ---> Tức nhịp 40 lần sẽ = 1 giây)

Ý anh muốn nói "vị trí quả lắc" là góc lệch của quả lắc so với trục cos (góc = 0) hoặc góc lệch so với vị trí ban đầu Shapes("Grp").Rotation = 0. Time sau khi trừ 1 số giây chẵn sẽ được 1 con sô biến thiên với chu kỳ 1 giây.

Có 1 vấn đề trong code của ndu, anh đọc mà không hiểu:

Shapes("Grp").Rotation = bao nhiêu, là 1 góc tính bằng độ . Thí dụ = 90 thì quả lắc nằm ngang chỉ sang phải.

Nhưng công thức của ndu gán Shapes("Grp").Rotation = sin của 1 góc nào đó, không phải là 1 góc nào đó.

Già rồi lẩm cẩm chăng?
 
Lần chỉnh sửa cuối:
Upvote 0
Ý anh muốn nói "vị trí quả lắc" là góc lệch của quả lắc so với trục cos (góc = 0).

Có 1 vấn đề trong code của ndu, anh đọc mà không hiểu:

Shapes("Grp").Rotation = bao nhiêu, là 1 góc tính bằng độ . Thí dụ = 90 thì quả lắc nằm ngang chỉ sang phải.

Nhưng công thức của ndu gán Shapes("Grp").Rotation = sin của 1 góc nào đó, không phải là 1 góc nào đó.

Già rồi lẩm cẩm chăng?
Nó hoạt động nhờ iCycle mà anh!
Cứ 25 ms thì hàm TimeProc phát ra 1 xung nhịp, khi ấy em sẽ cho iCycle = iCycle + 1 ---> Vì hàm SIN tuần hoàn nên dù iCycle có tăng lên bao nhiêu thì đến lúc nó cũng phải lập lại giá trị cũ
Quan trọng vẫn là tính chính xác ω thôi
--------------
Nhưng công thức của ndu gán Shapes("Grp").Rotation = sin của 1 góc nào đó, không phải là 1 góc nào đó.
Cái này chỉ có thể nói là... ĂN GIAN (để có được khả năng tuần hoàn)
Ẹc... Ẹc...
 
Upvote 0
Ăn gian cũng tốt, nhưng nếu tính chính thống thì có thể điều khiển góc dao động. Thí dụ với cách tính sau đây, con lắc dao động 50 độ (2 bên vị trí 0, mỗi bên 25 độ)

Mã:
 GrpNum = Round((Timer - Int(Timer)) * [COLOR="Red"]100[/COLOR], 0)
  GrpPos = Application.Min(GrpNum, 100 - GrpNum) - [COLOR="Red"]25[/COLOR]
....
    .Shapes("Grp").Rotation = GrpPos

Nếu thay 100 bằng 360 và 25 bằng 90, con lắc dao động 180 độ, mỗi bên 90 độ. Nhưng dứt khoát 1 chu kỳ là 1 giây, biên độ lớn thì tốc độ cao.

Trong file kèm theo, kéo scroll bar để thay đổi góc dao động.

Nhầm 1 tí về code của ndu. Trong cách tính toán ăn gian cũng có thể điều chỉnh góc dao động:
Mã:
.Shapes("Grp").Rotation = Sin(Atn(1) * (iCycle / 4 + 1 / 2)) * [COLOR="Red"][B]20[/B][/COLOR]

20 chính là góc dao động 1 bên!
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Ăn gian cũng tốt, nhưng nếu tính chính thống thì có thể điều khiển góc dao động. Thí dụ với cách tính sau đây, con lắc dao động 50 độ (2 bên vị trí 0, mỗi bên 25 độ)

Mã:
 GrpNum = Round((Timer - Int(Timer)) * [COLOR=Red]100[/COLOR], 0)
  GrpPos = Application.Min(GrpNum, 100 - GrpNum) - [COLOR=Red]25[/COLOR]
....
    .Shapes("Grp").Rotation = GrpPos
Cách của sư phụ cũng rất hay ---> Tuyệt chiêu nằm ở chổ: Timer - Int(Timer)
Ẹc... Ẹc...
Em đã có nghĩ qua làm sao lấy được phần ms nhưng lại chưa hề nghĩ đến công thức đơn giản này
Cảm ơn sư phụ
Chỉ có 1 chuyện liên quan đến.. thẩm mỹ ---> Nếu con lắc mà chuyển động "đều" quá thì nhìn lại chẳng giống con lắc thật
Sư phụ cứ để ý cái dùng hàm SIN của em với cái chuyển động đều của sư phụ sẽ cảm giác cái SIN ấy giống thật hơn!
----------------------------------
Còn 1 vấn đề nữa cần các cao thủ chỉ giáo:
- Trong sub StartTimer có đoạn: SetTimer Application.hWnd, 1, 25, AddressOf TimeProc ---> Tức 25ms sẽ phát ra 1 xung, vị chi trong 1s sẽ có 40 xung nhịp
- Và nếu đúng như vậy thì thì code điều khiển quả lắc phải là: Shapes("Grp").Rotation = Sin(Atn(1) * (iCycle / 5 + 1 / 2)) * 20 ---> Tức chia cho 5 chứ không phải chia cho 4
- Ấy vậy mà nếu chia cho 5 thì dao động của con lắc lại không theo đúng chu kỳ 1s
- Theo thí nghiệm thực tế thì 40 xung nhịp này bị "trễ" chỉ còn 32 xung
Chẳng hiểu sao lại như vậy?
 
Upvote 0
Nó hoạt động nhờ iCycle mà anh!
Cứ 25 ms thì hàm TimeProc phát ra 1 xung nhịp, khi ấy em sẽ cho iCycle = iCycle + 1 ---> Vì hàm SIN tuần hoàn nên dù iCycle có tăng lên bao nhiêu thì đến lúc nó cũng phải lập lại giá trị cũ
Quan trọng vẫn là tính chính xác ω thôi
--------------

Cái này chỉ có thể nói là... ĂN GIAN (để có được khả năng tuần hoàn)
Ẹc... Ẹc...
Cái đồng hồ quả lắc này nhiều khả năng chạy mãi sẽ có lúc nó bị khô dầu và quả lắc sẽ không lắc được nữa.
 
Upvote 0
Cái đồng hồ quả lắc này nhiều khả năng chạy mãi sẽ có lúc nó bị khô dầu và quả lắc sẽ không lắc được nữa.
Tôi đã nghĩ đến việc này rồi nhưng do lúc đầu chưa xác định được chính xác chu kỳ của nó nên chưa dùng Mod
Thay vì
iCycle = iCycle + 1
thì sửa thành:
iCycle = (iCycle Mod 32 ) + 1
Ẹc... Ẹc... khỏi khô dầu nhé
--------------------------
Cái đồng hồ trên vẫn còn 1 nhược điểm tôi chưa vừa ý lắm! Đó là khi chuông điểm giờ vang lên thì đồng hồ có vẽ bị "khựng" ---> Do dùng hàm Sleep
Nghĩ lại, ta có hàm SetTimer quá ngon, tội vì dùng Sleep cho mết
Cải tiến lại:
- Tách điều khiển kim đồng hồ và quả lắc ra riêng
- Tạo thêm 1 hàm điều khiển chuông báo giờ cũng dùng hàm SetTimer
Code như sau:
PHP:
Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Public iT As Long, sHour As Long, Pi As Double
PHP:
Sub StartTimer()
  StopTimer
  StopSound
  Pi = 4 * Atn(1)
  SetTimer Application.hWnd, 1, 1000, AddressOf HandControl
  SetTimer Application.hWnd, 2, 25, AddressOf ClockPen
End Sub
PHP:
Sub StopTimer()
  KillTimer Application.hWnd, 1
  KillTimer Application.hWnd, 2
End Sub
PHP:
Sub StartSound()
  OpenMidiOut (-1)
  Instrument = 14
  ret = midiOutShortMsg(handleMidiOut, (256 * Instrument) + 192)
  SetTimer Application.hWnd, 3, 1000, AddressOf Ponting
End Sub
PHP:
Sub StopSound()
  CloseMidiOut
  KillTimer Application.hWnd, 3
End Sub
PHP:
Private Function HandControl()
  Dim sTotal As Double, hPos As Double, mPos As Double, sPos As Double
  On Error Resume Next
  sTotal = Hour(Time) * 3600 + Minute(Time) * 60 + Second(Time)
  sPos = sTotal * 6 Mod 360
  mPos = (sTotal / 10) Mod 360
  hPos = (sTotal / 120) Mod 360
  With Sheet1
    .Shapes("sec").Rotation = sPos
    .Shapes("min").Rotation = mPos
    .Shapes("hrs").Rotation = hPos
  End With
  If sPos = 0 And mPos = 0 Then
    sHour = 1 * Left(Format(Time, "hh:mm:ss AM/PM"), 2)
    Call StartSound
  End If
End Function
PHP:
Private Function ClockPen()
  On Error Resume Next
  Sheet1.Shapes("cPen").Rotation = Sin(iT * Pi / 16) * 20
  iT = (iT Mod 32) + 1
End Function
PHP:
Private Function Ponting()
  sHour = sHour - 1
  PlayNote 0, 57, 120
  If sHour = -1 Then StopSound
End Function
Đương nhiên có kèm theo Module PlayMidi mới phát âm thanh được ---> Xem chi tiết trong file đính kèm
Bây giờ chuông báo giờ, kim đồng hồ cùng quả lắc đã chạy mượt mà hơn trước rồi
Không biết còn trục trặc gì không? Mời các bạn xem file và góp ý giúp tôi với
Cảm ơn!
 

File đính kèm

Upvote 0
Ẹc, Pi là từ khoá (const) có sẵn, đâu cần định nghĩa Pi = Atn(1) * 4 đâu ndu?

Thử xoá dòng định nghĩa xem, vẫn chạy tốt.
 
Upvote 0
Ẹc, Pi là từ khoá (const) có sẵn, đâu cần định nghĩa Pi = Atn(1) * 4 đâu ndu?

Thử xoá dòng định nghĩa xem, vẫn chạy tốt.
Vụ này em không biết à nha, cũng không tìm thấy Const Pi nó nằm chổ nào nữa
Thử xóa dòng định nghĩa nó cóc chạy!
Ẹc... Ẹc...
 
Upvote 0
Nhầm, khà khà khà!
Clock chạy Auto on Open. Pi khai báo public, nhấn stop Pi vẫn còn giá trị. Xoá định nghĩa, nhấn start, chạy tiếp vù vù.

Nhầm vì nhấn Help tìm Pi nó có ra kết quả. Coi kỹ lại, nó là Worksheet Function. Sorry sorry.

À, hình như đồng hồ mới quả lắc chạy vù vù, 1 chu kỳ nhỏ hơn 1 giây?
 
Upvote 0
À, hình như đồng hồ mới quả lắc chạy vù vù, 1 chu kỳ nhỏ hơn 1 giây?
Đây là 1 lỗi mà em chưa tìm ra được nguyên nhân từ đâu
- Khi thì nó chạy bình thường
- Có 1 lúc nào đó, tự dưng cái quả lắc lại chạy nhanh gấp đôi
- Có khi sư phụ đóng file rồi mở lại lần nữa, nó lại.. bình thường
--------------
Đang mò nảy giờ sư phụ ơi
 
Upvote 0
Tôi đã nghĩ đến việc này rồi nhưng do lúc đầu chưa xác định được chính xác chu kỳ của nó nên chưa dùng Mod
Thay vì
iCycle = iCycle + 1
thì sửa thành:
iCycle = (iCycle Mod 32 ) + 1
Ẹc... Ẹc... khỏi khô dầu nhé
Tôi nghĩ mãi mà chưa ra được ý nghĩa của con số 32, bác làm ơn giải thích dùm được không?
 
Upvote 0
Tôi nghĩ mãi mà chưa ra được ý nghĩa của con số 32, bác làm ơn giải thích dùm được không?
Lý ra thì nó là số 40 mới đúng ---> Vì SetTimer Application.hWnd, 1, 25, AddressOf TimeProc
Tôi đã nói ở bài 10 rồi mà... Chẳng hiểu sao khi chạy thì nó bị "trễ" gì đó không biết nữa, cuối cùng 40 chỉ còn lại 32
Hic
Bạn xem bài 10 và tìm nguyên nhân giúp tôi với
 
Upvote 0
À, hình như đồng hồ mới quả lắc chạy vù vù, 1 chu kỳ nhỏ hơn 1 giây?
Cuối cùng cũng tìm cách trị được lỗi này (đôi lúc mở file lên, quả lắc chạy nhanh hơn chu kỳ 1s)
Cách của tôi là tạo 1 name iT với công thức =TEXT(NOW(),"ss.00") ---> Dùng hàm RIGHT lấy 2 số cuối để làm biến chạy cho quả lắc
Đồng hồ này còn có thêm tờ lịch tháng nữa nha... Và đương nhiên, đúng giờ nó cũng sẽ gõ "boong boong" như cái đồng hồ thật sự

untitled1.JPG

untitled2.JPG

---------------------------------------
Các bạn tải về và test giúp xem còn gì trục trặc nữa không
 

File đính kèm

Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom