***
25-06-10, 08:18
Chào các bạn. Hiện nay công nghệ đã phát triển rất cao, chúng ta đã quá quen thuộc với 8051, chúng ta không từ bỏ 8051 nhưng sẽ dùng 8051 cho các thiết kế giản đơn, hoặc làm ngoại vi cho các hệ thống lớn hơn như ARM.
Công nghệ 8 bit rất hạn chế trong việc xử lý analog. xử lý giao diện đồ họa, xử lý giao tiếp TCP/IP, USB v.v... Nếu bạn chỉ làm ứng dụng nho nhỏ trong phòng thí nghiệm như đo nhiệt độ hiển thị LED 7 đoạn hoặc 2x16 LCD thì 8051 làm tốt, nhưng nếu các bạn muốn đo nhiệt độ trong công nghiệp với độ chính xác 2 số thập phân, truyền qua mạng LAN hiển thị trên Color graphic LCD thì chỉ có ARM là đáp ứng tốt nhất...Vì thế, vì công nghệ ngày càng phát triển chúng ta hãy...để mắt tới ARM.
Bản thân tôi đang vừa đi làm kiếm tiền nuôi vợ con vừa nghiên cứu ARM nên thời gian lên diễn đàn rất ít. Tuy nhiên, vì sự phát triển của ngành điện tử nước nhà, vì muốn hướng các bạn trẻ đến một dòng cao cấp hơn, và vì...$(hơi thô thiển một chút), tôi sẽ cố gắng chia sẻ những gì học được trên diễn đàn này. Và hy vọng các cao thủ trên diễn đàn này sẽ chỉ bảo thêm để sao cho các thế hệ sau chúng ta sẽ ngày càng phát triển hơn.
Viet Vinh
25-06-10, 08:30
Để làm quen với ARM theo tôi thì tốt nhất là bắt đầu bằng một ví dụ đơn giản nhất. Tôi sẽ lấy ví dụ 2 LED nhấp nháy để bắt đầu. Kit phát triển và mạch nạp mua ở TME. Dòng ARM tôi đang dùng là AT91SAM7S256.
Viet Vinh
25-06-10, 08:39
Đầu tiên, phải nói đến các ngắt (interrupt) ARM có các ngắt như sau:
RESET, Undefined instruction,Software interrupt,Prefetch abort,Data abort,Reserved,Interrupt request và Fast interrupt request.
Chúng ta hãy từ từ để ý đến các ngắt khác. Hiện tại chúng ta chỉ để ý đến ngắt RESET là ngắt xảy ra khi chúng ta bật nguồn lên, lúc đó AT91SAM7S256 chưa được remap nên sẽ xem vùng FLASH bắt đầu ở địa chỉ 0x00000000 và sẽ thi hành lệnh đầu tiên tại đây.
NMI
25-06-10, 08:58
Hay đấy bác ạ, ủng hộ bác viết thêm ^^
Viet Vinh
25-06-10, 09:08
Đây là đoạn code đầu tiên trong startup code:
RESET: B ThietLapHeThong ;RESET
UNDEF: B UNDEF ;UNDEF
SWI: B SWI ;SWI
PABT: B PABT ;PABT
DABT: B DABT ;DABT
RESV: B RESV ;RESERVED
IRQ: B Interrpt ;IRQ
FIQ: //Đoạn code đầu tiên cho Fast Interrupt ở đây
....
....
Chú ý khi khởi động, ARM sẽ mặc định ở chế độ 32 bit. Như vậy độ dài mỗi lệnh là 4byte. Chỉ khi nào ta cố ý chuyển qua chế độ THUMB thì độ dài lệnh sẽ là 16 bit. Sau khi compile và download dòng đầu tiên phải ở địa chỉ 0x00000000 trong FLASH, dòng kế tiếp là 0x00000004, 0x00000008, 0x0000000c,0x00000010...Các địa chỉ này chính là các interruptvector của ARM.
Không như 8051, Trước khi khởi động ARM phải được thiết lập các thông số cho interrupt, Clock, stack...
Ở 8051 ta không khởi động Interrupt vì nó mặc định là không sử dụng, ta không khởi động clock vì nó hoạt động ở xung nhịp thấp và cố định (ở AT91SAM7S256 khi khởi động nó sẽ khởi động ở SlowClock để tiết kiệm điện), ta không khởi động stack vì chương trình nhỏ.
Do các interrupt là luôn enable nên các interrupt không xài ta phải cho nhảy tại chỗ, để tránh luồng xử lý đi hoang( thanh ghi lệnh chứa random value). Như vậy các lệnh sau phục vụ cho mục đích này:
UNDEF: B UNDEF ;UNDEF
SWI: B SWI ;SWI
PABT: B PABT ;PABT
DABT: B DABT ;DABT
RESV: B RESV ;RESERVED
Ở đây B là lệnh Branch (rẽ nhánh). "SWI: B SWI" là lệnh nhảy tại chỗ dùng để bẫy chương trình tại đây, tránh luồng xử lý đi hoang gây hư hại hệ thống ngoại vi.
Lệnh này :"RESET: B ThietLapHeThong" sẽ được thi hành khi bật nguồn. Nó sẽ nhảy đến chương trình khởi động hệ thống mà tôi sẽ đề cập trong lần sau.
bluechip
25-06-10, 09:42
Hoan hô hi vọng thread này sôi động đây !!!!!
Dùng trình biên dịch gì để biên dịch vậy bạn ơi ?
bapt_24588
25-06-10, 10:04
ủng hộ bác nhá.em học về 8051 roài.Nhưng chưa viết được tốt lắm.bác có kinh nghiệm nào học ARM tốt thì chia sẻ cho mọi người nhé
Viet Vinh
25-06-10, 11:16
Trước khi tiếp tục, các bạn hãy tham khảo trước đoạn code dưới đây:
RESET: B ThietLapHeThong ;RESET
UNDEF: B UNDEF ;Undefined interrupt
SWI: B SWI ;Software interrupt
PABT: B PABT ;Prefetch Abort
DABT: B DABT ;Data Arbot
RESV: B RESV ;RESERVED
IRQ: B Interrpt ;Interrupt request
FIQ: //Đoạn code đầu tiên cho Fast Interrupt ở đây
....
....
ThietLapHeThong: LDR SP,=RAM_TOP ;Thiết lập stack pointer ở đầu RAM(Nơi có địa chỉ cao nhất)
LDR R0,=AT91C_KhoiTao ;Tải địa chỉ hàm thiết lập cho ATMEL AT91SAM7Sxxx
MOV LR,PC ;Ghi lại địa chỉ trở về
BX R0 ;Chuyển qua đoạn chương trình con AT91C_KhoiTao
LDR R0,=RAM_TOP ;Tải lại stack pointer cho các core mode khác
MSR CPSR_C,#MASK_I|MASK_F|ARM|FIQ_MODE ; change core mode
LDR R8,=ATMEL AT91SAM7Sxxx_AIC ;set banked r8
MSR CPSR_C,#MASK_I|MASK_F|ARM|IRQ_MODE ; change core mode
MOV SP,R0 ;Thiêt lập stack cho chế độ ngắt
SUB R0,R0,#64 ;Khoi dong stack cho Supervision mode
MSR CPSR_C,#PERMIT_I|PERMIT_F|ARM|SVC_MODE; change core mode
MOV SP,R0 ;Thiêt lập stack cho chế độ supervision
LDR R0,=TaiFileHeThong ;Tải địa chỉ hàm TaiFileHeThong
MOV LR,PC ;Ghi lại địa chỉ trở về
BX R0 ;gọi hàm TaiFileHeThong
LDR LR,=Thoat ;Ghi lại địa chỉ trở về
LDR R0,=Main ;Tai dia chi ham Main
BX R0 ;Goi ham Main
Thoat: B Thoat
Các bạn hãy lên mạng tìm hiểu trước về:
-Kiến trúc ARM, các chế độ core mode, các thanh ghi và banked thanh ghi. AVR và THUMB mode
-Kiến trúc AT91SAM7Sxxx, Main clock, Slowclock, Phase Locked Loop clock, Master clock, Flash, Interrupt, fast interrupt.
I'll soon be back.
phuchiepjsc
25-06-10, 11:20
Tôi nghĩ học ARM các bạn nên tiếp cận với những ngôn ngữ cao cấp như C .... Bởi 1 chip có sức mạnh ... sẽ dùng để giải những bài toán lớn, quy mô... mà với những bài toán đó nếu viết bằng ASM sẽ rất khó khăn và vất vả trong khâu rà soát và sửa lỗi ... hay chèn thêm các chức năng ... Kinh nghiệm đã chỉ cho tôi như vậy . Bạn không định dùng ARM để làm mấy cái nhấp nháy xanh đỏ chứ ???.
Với lập trình C , khi có vướng mắc cũng dễ " điều tra ", tư vấn ... còn ASM ...nhìn một tí là bùng nhùng hết mắt... Kể cả những người rành về ASM cũng nản và không muốn trợ giúp !
Viet Vinh
25-06-10, 11:31
Hoan hô hi vọng thread này sôi động đây !!!!!
Dùng trình biên dịch gì để biên dịch vậy bạn ơi ?
Hè, hè
Mình học hành hơi khác ...người một chút (nhưng hiệu quả)
Đó là:
-Mua kít phát triển và mạch nạp(vì mình chẳng có thời gian làm mạch)
-Lên mạng tìm một file hex đơn giản nào đó về download vào kít phát triển xem nó chạy tốt hay không?
-Giải mã file hex đó (ở đây là chương trình bliking LED) ra Asembler. Cái này có vẻ khó nghe, nhưng vì mình muốn đi chuyên sâu nên đây là con đường duy nhất. Lợi thế của mình là biết lập trình WindowsAPI và Visual C++ nên mình đã viết thành công phần mềm mã hóa và giải mã ARM code by hand. Mình đang tiếp tục viết bộ compiler cho ARM tuy rằng khó nhưng cũng không còn cách nào khác.
-Vì mình chưa viết chương trình nào cả nên mình cũng chưa dùng trình biên dịch nào cả.
Các trình biên dịch như armasm có sẵn trong WinXP nhưng nó đòi bản quyền=> không thích
Chúc vui.
Viet Vinh
25-06-10, 13:24
Tôi nghĩ học ARM các bạn nên tiếp cận với những ngôn ngữ cao cấp như C .... Bởi 1 chip có sức mạnh ... sẽ dùng để giải những bài toán lớn, quy mô... mà với những bài toán đó nếu viết bằng ASM sẽ rất khó khăn và vất vả trong khâu rà soát và sửa lỗi ... hay chèn thêm các chức năng ... Kinh nghiệm đã chỉ cho tôi như vậy . Bạn không định dùng ARM để làm mấy cái nhấp nháy xanh đỏ chứ ???.
Với lập trình C , khi có vướng mắc cũng dễ " điều tra ", tư vấn ... còn ASM ...nhìn một tí là bùng nhùng hết mắt... Kể cả những người rành về ASM cũng nản và không muốn trợ giúp !
Bạn nói rất đúng dùng C rất dễ hiểu, gỡ rối và nhanh chóng. Nói chung C không thể chê vào đâu được. Tuy nhiên các bạn nên biết cả C lẫn Assembler. Khi làm hàng thương mại, C là một lựa chọn tối ưu. Chúng ta viết Assembler cho những chương trình nhỏ mang tính can thiệp sâu vào hệ thống như tạo và sửa CStartup cho C, Boot loader... những cái mà C không thể can thiệp được và cũng không thể thiếu được.
Tôi lấy ví dụ đơn giản: Giả sử bạn có một hệ thống dùng ARM, bạn muốn các chương trình của bạn viết trên Windows sau đó tải vào thẻ MMC hay SD và đem thẻ nhớ đó vào hệ thống ARM và chạy trên đó. Như vậy trong ARM phải tích hợp một boot loader, mà cái này thì chỉ có thể viết bằng ASSEMBLER.
Cho nên tôi nghĩ các bạn phải biết cả hai (ASSEMBLER và C), C cho các ứng dụng chính, còn Assembler cho các code can thiệp sâu vào hệ thống.
C thì có lẽ rất nhiều người biết, còn Assembler thì tôi không dám chắc.
Đó chỉ là các ý kiến cá nhân của tôi. Chúc vui.
itx
25-06-10, 13:26
Để bắt đầu với các dòng xử lý 32bit con đường nhanh và dễ dàng nhất là sử dụng một ngôn ngữ lập trình cấp cao như C, C++ .
Assembler được sử dụng khi bạn muốn tìm hiểu sâu về ARM về cách thức hoạt động của Core ARM. Chỉ nên bắt đầu việc này khi bạn đã thân thộc với ARM. Vì Core ARM không giống như Core 8051 đơn giản và chỉ có một loại core, ARM có khoảng 70 loại core khác nhau và còn tiếp tục tăng thêm, chưa kể đến các thiết bị ngoại vi mà các nhà sx khác nhau thêm vào ví dụ chỉ thằng Atmel với core ARM7TDMI đã có khoảng 37 loại chíp khác nhau, chưa kể đến hàng trăm nhà sx như Atmel, NXP ...... mỗi nhà sx khác nhau lại có một vài thay đổi, sự khác nhau này là không đáng phải quan tâm với các ngôn ngữ lập trình cấp cao nhưng nếu bạn sử dụng Assembler thì lại là chuyện khác code của bạn sẽ phải thay đổi khoảng 20->40% để thích nghi, điều này có nghĩa là bạn sẽ phải viết và học lại khi bạn chuyển từ loại chip này sang chip khác cho dù cùng core.
ARM được thiết kế để lập trình với ngôn ngữ cấp cao vì vậy khi sử dụng Assembler bạn đã tự trói tay bạn lại.
Các trình biên dịch C, Assembler cho ARM đều có phiên bản mã nguồn mở có thể xem chi tiết tại đây
[You must be registered and logged in to see this link.] .Các trình dịch ngược ( hex, bin -> ASM ) đều có phiên bản miễn phí, mã nguồn mở lẫn thương mại.
Viết compiler cho ARM ? Chắc bạn chưa tưởng tượng được đây là việc lớn đến mức nào, nên nhớ Việt Nam bây giờ thậm chí còn chưa có compiler cho 8051 ( dòng VDK đơn giản và thông dụng nhất ) made in Vietnam
vì độ phức tạp và " viết cũng chẵng để làm gì " của nó.
Khi dịch ngược bạn có thể đưa bất kỳ một file firmware (.hex, .bin....) nào của các loại mcu khác ( 8051, pic, avr ...) trình dịch ngược đều đưa ra ASM cho bạn nhưng cái file ASM đó hoàn toàn sai, nếu muốn cho ra file ASM đúng trình dịch ngược phải có profile chính xác của MCU cần thiết. Nói thì đơn giản nhưng nếu có đầy đủ công cụ cần thiết một Hacker muốn hack firmware như cách của bạn thành công cũng phải mất vài tuần nếu anh ta may mắn.
itx
25-06-10, 13:41
Loader cho ARM có thể viết bằng bất kỳ ngôn ngữ nào.
Lưu ý là ARM có hai loại bootloaders chính, một loại dùng để tải firmware vào chip, loại này thường được nhà sản xuất tích hợp sẵn trong chip. Một loại là mã khởi động như U-Boot ( hay còn gọi là bộ tải khởi động ) dùng để khởi động hệ điều hành (Linux, wince....).
Viet Vinh
25-06-10, 14:03
Để bắt đầu với các dòng xử lý 32bit con đường nhanh và dễ dàng nhất là sử dụng một ngôn ngữ lập trình cấp cao như C, C++ .
Assembler được sử dụng khi bạn muốn tìm hiểu sâu về ARM về cách thức hoạt động của Core ARM. Chỉ nên bắt đầu việc này khi bạn đã thân thộc với ARM. Vì Core ARM không giống như Core 8051 đơn giản và chỉ có một loại core, ARM có khoảng 70 loại core khác nhau và còn tiếp tục tăng thêm, chưa kể đến các thiết bị ngoại vi mà các nhà sx khác nhau thêm vào ví dụ chỉ thằng Atmel với core ARM7TDMI đã có khoảng 37 loại chíp khác nhau, chưa kể đến hàng trăm nhà sx như Atmel, NXP ...... mỗi nhà sx khác nhau lại có một vài thay đổi, sự khác nhau này là không đáng phải quan tâm với các ngôn ngữ lập trình cấp cao nhưng nếu bạn sử dụng Assembler thì lại là chuyện khác code của bạn sẽ phải thay đổi khoảng 20->40% để thích nghi, điều này có nghĩa là bạn sẽ phải viết và học lại khi bạn chuyển từ loại chip này sang chip khác cho dù cùng core.
ARM được thiết kế để lập trình với ngôn ngữ cấp cao vì vậy khi sử dụng Assembler bạn đã tự trói tay bạn lại.
Các trình biên dịch C, Assembler cho ARM đều có phiên bản mã nguồn mở có thể xem chi tiết tại đây
[You must be registered and logged in to see this link.] .Các trình dịch ngược ( hex, bin -> ASM ) đều có phiên bản miễn phí, mã nguồn mở lẫn thương mại.
Viết compiler cho ARM ? Chắc bạn chưa tưởng tượng được đây là việc lớn đến mức nào, nên nhớ Việt Nam bây giờ thậm chí còn chưa có compiler cho 8051 ( dòng VDK đơn giản và thông dụng nhất ) made in Vietnam
vì độ phức tạp và " viết cũng chẵng để làm gì " của nó.
Khi dịch ngược bạn có thể đưa bất kỳ một file firmware (.hex, .bin....) nào của các loại mcu khác ( 8051, pic, avr ...) trình dịch ngược đều đưa ra ASM cho bạn nhưng cái file ASM đó hoàn toàn sai, nếu muốn cho ra file ASM đúng trình dịch ngược phải có profile chính xác của MCU cần thiết. Nói thì đơn giản nhưng nếu có đầy đủ công cụ cần thiết một Hacker muốn hack firmware như cách của bạn thành công cũng phải mất vài tuần nếu anh ta may mắn.
Cám ơn bạn đã cho lời khuyên hữu ích.
Tôi không nghĩ là tôi sẽ phát triển ứng dụng trên ASSEMBLER, cũng như bạn nghĩ. Bởi vì một ứng dụng nhỏ nhưng code assembler dài khủng khiếp. Cụ thể là chỉ có chương trình nháy LED mà tôi dịch ra 5 trang A4 cỡ chữ=8.
Có lẽ bởi vì bạn là cao thủ rồi nên có lẽ vấn đề này là thừa. Nhưng với tôi, khi đọc datasheet của ATMEL nó nói vậy thì biết vậy cũng chán, nên tìm một ví dụ đơng giản để đối chiếu cho dễ hình dung, và vì vậy tôi đã bỏ công dịch ngược và cũng tìm thấy nhiều thú vị nên muốn chia sẻ cùng các bạn.
Còn về C, bản thân tôi cũng có nhiều kinh nghiệm về C/C++ nên việc đọc code C của tôi cũng không tệ lắm. Đúng là tôi cũng có thiếu sót trong việc không đưa code C vào đây. Rút kinh nghiệm và theo như như các cao kiến của các bạn, tôi sẽ kết hợp cả C và ASSEMBLER trong chương trình nháy LED này. Hy vọng sẽ giúp ích được cho các bạn và cho tôi nữa.
Tôi chọn chương trình nháy LED làm nền tảng vì nó đơng giản, chứ tôi không muốn các bạn dùng ARM để làm chương trình nháy LED (Các bạn đừng hiểu nhầm nha).
ARM có rất nhiều core, nhưng chúng có tính kế thừa và tương thích ngược nên các bạn đừng lo. Hơn nữa dù có khác hơi nhiều như dòng Cortext gì đó thì cũng không sao, cáo quan trọng là bạn đã có nền tảng về nó.
Đúng là hiện nay có nhiều compiler cho ARM. Hy vọng các bạn cao thủ hướng dẫn thêm cho các hậu bối. Riêng tôi, dùng bộ compiler nào cũng tốt. Chỉ vì muốn thử sức mình nên muốn tạo compiler riêng (chỉ vậy thôi).
Vấn đề là thấy chúng ta cứ dậm chân tại chỗ với dòng 8-bit nhiều quá nên chỉ muốn mọi người nhìn xa hơn một chút. Thế giới họ tiến xa quá rồi...
Chúc tất cả vui vẻ
Viet Vinh
25-06-10, 14:23
Viết compiler cho ARM ? Chắc bạn chưa tưởng tượng được đây là việc lớn đến mức nào, nên nhớ Việt Nam bây giờ thậm chí còn chưa có compiler cho 8051 ( dòng VDK đơn giản và thông dụng nhất ) made in Vietnam
vì độ phức tạp và " viết cũng chẵng để làm gì " của nó.
Cũng không nên bi quan chứ, bạn xem thử coi, dù chưa hoàn thiện nhưng cũng đâu có ...quá tệ phải không?
itx
25-06-10, 15:21
Bạn đang nhầm lẫn giữa compiler và graphical user interface (GUI)
Một compiler tốt khi mã thực thi nó sinh ra không có rác hay mã thừa, compiler không phải là GUI giao diện lòe loẹt. Bạn hãy tham khảo GCC của GNU đây là một compiler tốt và nếu có ý định làm compiler hãy tải về mã nguồn của nó.
Và xem qua cái này
[tut] eclipse and ARM trên Windows (http://www.dientuvietnam.net/forums/showthread.php?t=32403)
Viet Vinh
25-06-10, 15:28
ARM có 37 thanh ghi trong đó có 20 thanh ghi sẽ được dùng theo processor mode gọi là các banked register.
Thông thường có 17 thanh ghi khả dụng trong User mode. đó là các thanh ghi r0-R15 và CPSR.
R0,R1,R2,R3 và R12 thường được xem là các thanh ghi nháp (scratch registers) các thanh ghi R13, R14 và R15 lần lượt là Stack pointer(sp) link register (lr) và Program counter(pc).
Khác với 8051, mỗi khi 8051 gọi một chương trình con thanh ghi lệnh(PC) tự động được đẩy vào stack. Khi return từ chương trình con PC tự động được pop up ra khỏi stack.
Với ARM do khi gọi chương trình con ARM cho phép chuyển tập lệnh từ ARM sang THUMB và ngược lại nên tập lệnh cụa ARM cho phép sử dụng một thanh ghi liên kết Link register để lưu địa chi trả về.
Như vậy thay vì được đẩy vào stack thì PC được lưu vào link register làm cho hệ thống linh hoạt hơn.
Viet Vinh
25-06-10, 15:38
Bạn đang nhầm lẫn giữa compiler và graphical user interface (GUI)
Một compiler tốt khi mã thực thi nó sinh ra không có rác hay mã thừa, compiler không phải là GUI giao diện lòe loẹt. Bạn hãy tham khảo GCC của GNU đây là một compiler tốt và nếu có ý định làm compiler hãy tải về mã nguồn của nó.
Và xem qua cái này
[tut] eclipse and ARM trên Windows (http://www.dientuvietnam.net/forums/showthread.php?t=32403)
(Chà không ngờ mình lại lạc đề quá)
Thôi thì cũng nói rõ, là khi lập trình mình thường hay dùng tính kế thừa. Giao diện trên bản chất nó là 1 chương trình HMI dùng để điều khiển một nhà máy thủy điện có 5 tổ máy H1,2,3,4 và 5 .Do mình lấy source code đó để tiết kiệm thời gian nên ...tàn dư của nó làm bạn hiểu nhầm.
Thực ra compiler này mình dự định viết cho ARM nhưng chỉ là nháp thôi. Mục đích của mình là viết compiler để củng cố user code cho HMI của mình. Để tránh lạc đề chắc tôi nghĩ chúng ta nên dừng compiler ở đây nha. Bạn có nghĩ như vậy không?
Thanks anyway
phuchiepjsc
25-06-10, 16:25
không có gì tốt hơn là học qua cấc ví dụ mẫu. bạn thử xem ... nếu có sự giải thích tương quan giữa arm với cấu trúc lệnh của 8051, pic (các mcu khác ) thì những người chưa biết sẽ đỡ ngỡ ngàng hơn.
Viet Vinh
25-06-10, 16:33
Nói về stack, bản chất stack là 1 vùng nhớ trên RAM dùng để lưu nhanh, tạm thời các biến hoặc thanh ghi trước khi mượn các biến hay thanh ghi này cho mục đích khác.
Stack rất có ich khi viết các chương trình con. Toàn bộ background của chương trình mẹ sẽ được đẩy vào stack nhường lại context cho đoạn chương trình con.
Khi chương trình con thực hiện xong toàn bộ background của chương trình mẹ sẽ được tái phục hồi lại nhanh chóng từ stack.
Trong ARM và có lẽ các core khác cũng vậy. Nếu interrupt vectors và usercode đi đằng này lại thì stack dĩ nhiên phải đi từ đằng kia sang để tránh stack overlapped.
Vì vậy, nếu interrupt vectors bắt đầu từ 0x00000000 thì stack nên bắt đầu từ địa chỉ khả dụng cao nhất trong RAM với 16kBRAM stack pointer nên bắt đầu từ 16*1024=4000H.
Vì Vùng SRAM của ATMEL AT91SAM7Sxxx bắt đầu từ 0x0020 0000 nên stack sẽ bắt đầu từ 0020 4000H. Đối với dòng có SRAM=32, 512, 128 hay 256kB cũng tính tương tự như vậy.
Giá trị này ATMEL nó gọi là RAM_TOP. Vì vậy dòng đầu tiên của hàm ThietLapHeThong là:
LDR SP,=RAM_TOP tức là khởi tạo stack tại vị trí đầu RAM( Địa chỉ cao nhất).
Khi đã khởi tạo stack xong việc đầu tiên là nó thiết lập cấu hình cho ATMEL AT91SAM7Sxxx. Ta sẽ nói rõ việc khởi tạo đó sau, chủ yếu là chọn xung nhịp hệ thống, cấu hình nút reset, thiết lập wait state để tối ưu THUMB code...
Sau đó thiết lập stack cho các processor mode khác. Xem xét tải file tự chạy như hệ điều hành...Và cuối cùng là nhảy đến hàm Main.
Thôi đến đây là weekend rồi, chúc các bạn những ngày nghỉ vui vẻ.
(Sẽ sớm trở lại)
Viet Vinh
25-06-10, 16:37
không có gì tốt hơn là học qua cấc ví dụ mẫu. bạn thử xem ... nếu có sự giải thích tương quan giữa arm với cấu trúc lệnh của 8051, pic (các mcu khác ) thì những người chưa biết sẽ đỡ ngỡ ngàng hơn.
Thanks. Mình sẽ cố gắng diễn giải liên quan đến 8051 (Mình chưa xài PIC và AVR) liên quan đến C và ASSEMBLER.
Chúc bạn những ngày cuối tuần vui vẻ
Viet Vinh
28-06-10, 08:42
Hi again.
Tuần vừa rồi tôi đã trình bày cho các bạn 2/3 phần CStartup code.
Hôm nay tôi xin trình bày tiếp về Processor mode:
8051 chỉ có một mode duy nhất nên không có khái niệm mode trong 8051.
Với ARM, vốn được thiết kế để có thể chạy một hệ điều hành nào đó, như hệ thống nhúng chẳng hạn, nên nó được tích hợp một số chế độ hoạt động.
ARM có tất cả 7 chế độ xử lý trong đó 6 chế độ toàn quyền (nonprivileged) là abort, fast interrupt request, interrupt request, supervisor, system, và undefined và một chế độ hạn chế (privileged) là user.
Khi truy cập bộ nhớ không thành công ARM sẽ tự chuyển qua chế độ abort.
Khi có một ngắt xảy ra ARM sẽ tự chuyển qua chế độ fast interrupt request hay interrupt request.
Khi xử lý một lệnh không nằm trong tập lệnh vì nhiều lý do ARM sẽ tự chuyển qua chế độ Undefined.
Khi hệ thống reset ARM ở chế độ supervisor là nơi mà hệ điều hành hoạt động.
Chế độ user là chế độ được bảo vệ dùng để chạy các chương trình ứng dụng.
Chế độ system là một chế độ đặc biệt của chế độ user.
Mỗi chế độ có một context riêng, các thanh ghi được hoán chuyển (banked) và mức độ điều khiển hệ thống riêng.
Đối với 8051 khi có một lỗi xảy ra, hệ thống hoặc là bị treo hoặc là chạy lộn xộn. Nhờ có nhiều chế độ xử lý và nhờ sự sắp xếp hợp lý của các phần mềm hệ thống (system software) ARM sẽ luôn được bảo vệ khi có một lỗi hệ thống xảy ra.
Để phục vụ cho các ứng dụng giản đơn, đoạn CStartup ở trên chỉ khởi tạo stack cho chế độ interrupt request và interrupt request. Hàm main sẽ được thực hiện ở supervisor mode.
Đối với một chương trình có hệ thống hàm Main sẽ được đặt trong một chế độ an toàn hơn đó là user mode. Tuy nhiên, với trình độ sơ đẳng của chúng ta, như thế đã là quá đủ.
Có lẽ các bạn chưa sẵn sàng để làm quen với ASSEMBLER nên tôi sẽ vắn tắt phần CStartup như thế. Bây giờ chúng ta hãy xem ATMEL AT91SAM7Sxxx được khởi động như thế nào.
Như đã đề cập trước, sau khi thiết lập stack xong, chương trình Cstartup sẽ gọi hàm AT91C_KhoiTao tức là hàm LowLevelInit trong file lowlevelinit.c ta hãy xem qua đoạn chương trình này.
Ban đầu các file sau phải được include
// Include the board file description
#include "AT91SAM7S256.h"
#include "Board.h"
file AT91SAM7S256.h được viết bởi ATMEL dùng để định nghĩa toàn bộ các thanh ghi và địa chỉ của AT91SAM7S256 sẵn sàng được truy cập bởi C code.
File Board.h được định nghĩa bởi người dùng để định nghĩa tất cả những gì người dùng muốn.
Các hàm sau được định nghĩa trong CStartup nhưng vì được dùng lại ở đây nên phải khai báo dạng external
extern void AT91F_Spurious_handler(void);
extern void AT91F_Default_IRQ_handler(void);
extern void AT91F_Default_FIQ_handler(void);
Vì trong chương trình nháy LED ta không sử dụng interrupt nên trong CStartup ta bẫy luồng xử lý tại đây.
:
AT91F_Default_FIQ_handler: b AT91F_Default_FIQ_handler
AT91F_Default_IRQ_handler: b AT91F_Default_IRQ_handler
AT91F_Spurious_handler: b AT91F_Spurious_handler
Công việc đầu tiên phải làm là set Flash Wait State. Việc này khá lạ đối với 8051, nhưng như bạn có thể thấy là ARM hoạt động với xung clock cao hơn. Một khi vi điều khiển hoạt động nhanh hơn bộ nhớ FLASH thì cần phải có thời gian chờ.
Xung nhịp cho AT91SAM7Sxxx bao gồm Main clock, Slowclock, Phase Locked Loop clock một trong 3 clock đó sẽ được chọn làm Master clock. Lúc đầu, AT91SAM7Sxxx được khởi động với Slow clock nên không cần phải quan tâm đến Flash wait state.
Nhưng khi chuyển qua xung nhịp cao hơn thì việc đầu tiên là phải chọn Flash Wait State. Việc chọn thời gian chờ tùy thuộc vào tần số hoạt động của vi điều khiển (Cái này phải xem thêm trong data sheet phần AC characteristic)
Theo data sheet của AT91SAM7Sxxx thì với tần số hoạt động từ 0->30MHz: không cần thời gian chờ (FWS=0), từ 30->55MHz thì cần 1 thời gian chờ (FWS=1)
Vì tần số hoạt động Master clock chúng ta sẽ chọn để truy cập FLASH là 50MHz nên FWS=1. Như vậy trong thanh ghi Flash mode register ta cài đặt:FLASH SETTING=50MASTER CLOCK CYCLES/1MICROSECOND và FWS=1
Dùng C:
AT91PS_PMC pPMC = AT91C_BASE_PMC;
AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN)&(50 <<16)) | AT91C_MC_FWS_1FWS;
Dùng ASSEMBLER:
MVN R0,=MC_FMR ;R0=FFFFFF60 //MC_FMR //MEMORY CONTROLLER-FLASH MODE REGISTER
LDR R1,=00320100 ;//FLASH SETTING=50MASTER CLOCK CYCLES/1MICROSECOND|FWS=1
STR R1,[R0,#0] ;APPLY SETTING
Sorry mình nhầm
Phải là như thế này:
Dùng ASSEMBLER:
MOV R0,#9FH
MVN R0,R0 ;R0=FFFFFF60 //MC_FMR //MEMORY CONTROLLER-FLASH MODE REGISTER
LDR R1,=00320100 ;//FLASH SETTING=50MASTER CLOCK CYCLES/1MICROSECOND|FWS=1
STR R1,[R0,#0] ;APPLY SETTING
Viet Vinh
28-06-10, 15:44
(tiếp theo)
Vì Watchdog timer không dùng nên ta dissable nó:
Dùng C:
//* Watchdog Disable
// result: AT91C_WDTC_WDMR = 0x00008000 (Watchdog Mode Register)
AT91C_BASE_WDTC->WDTC_WDMR= AT91C_WDTC_WDDIS;
Dùng assembler:
LDR R0,=WDT_MR ;//WDT_MR=WATCHDOG TIMER-MODE REGISTER
MOV R1,#80H
LSL R1,R1,#8 ;DISABLE WATCHDOG TIMER
STR R1,[R0,#0] ;APPLY SETTING
Tiếp đó là chọn startup time cho Main clock.
Theo data sheet của AT91SAM7Sxxx thì:
RC oscillator frequency range in kHz:22≤fRC≤42
Oscillator frequency range in MHz:3≤fOsc≤20
Main clock start up time:
CS = 3 pF(1) 1/(tCPMAIN) = 3 MHz: 14.5ms
CS = 7 pF(1) 1/(tCPMAIN) = 16 MHz: 1.4ms
CS = 7 pF(1) 1/(tCPMAIN) = 20 MHz: 1ms
Theo board mạch đang dùng thì:
Crystal frequency = 18.432MHz=>tstartup<1.4ms
Ta có OSCOUNT*(8*tSLCK)=tstartup=>OSCOUNT=tstartup/(8*tSLCK) như vậy
OSCOUNTmax=tstartupmax/(8*tSLCKmin).
Vì tSLCKmin=1/fRCmax=1/42kHz=23.8us;
=>OSCOUNTmax=1.4ms/(8*23.8us)=7.35;
Ở đây ta chọn OSCOUNT=6; tstartup=6*(8*tSLCK)=48*tSLCK.
Ứng với tần số Slowclock=48/1.4ms=34.3kHz.
Sau khi chọn startup time và kích hoạt Mainclock. Chương trình cầ phải chờ cho main clock ổn định bằng cách đọc bit MOSCS trong thanh ghi status register
Dùng C:
pPMC->PMC_MOR = (( AT91C_CKGR_OSCOUNT & (0x06 <<
| AT91C_CKGR_MOSCEN ));
// Wait the startup time
while(!(pPMC->PMC_SR & AT91C_PMC_MOSCS));
Dùng Assembler:
LDR R0,=AT91SAM7SXXX_CKGR_MOR ;Main Oscillator Register
LDR R1,=00000601H ;//;SETTING :OSCOUNT=6(48 SlowClockCycles)|Main Oscillator is enabled
STR R1,[R0,#0] ;APPLY SETTING
LDR R0=AT91SAM7SXXX_PMC_SR ;//POWER MANAGEMENT-STATUS REGISTER
MCK_WAIT: LDR R1,[R0,#0] ;LOAD CURRENT STATUS
LSL R1,R1,#31 ;CHECK BIT 0//BIT MOSCS-Main oscillator is stabilized if SET
BPL MCK_WAIT ;WAIT FOR MAIN OSCILLATOR STABLE
Cũng giống như Main oscillator, PLL oscillator cũng cần được cung cấp thời gian startup và phải chờ cho tới khi ổn định.
Theo DC charcteristic của AT91SAM7S256 thì:
PLL Fin=1->32MHz.
PLL Fout=80->160MHz (Field out of CKGR_PLL is:00)
PLL Fout=150->180MHz (Field out of CKGR_PLL is:10)
Sau khi Main clock ổn định với tần số thạch anh 18.432MHz ta dùng tần số này để đưa vào mạch PLL để tạo ra tần số PLL clock mong muốn.
Trước hết ta phải chọn dải tần số ra.
Nếu chọn tần số ra trong dải 80-160MHz thì chọn OUT[2] trong thanh ghi AT91C_CKGR_PLLR=00.
Nếu chọn tần số ra trong dải 150-180MHz thì chọn OUT[2] trong thanh ghi AT91C_CKGR_PLLR=10.
Ví dụ: nếu ta chọn MUL=26; DIV=5 thì:
PLLCLK=MainClock*(MUL+1)/DIV=18.432MHz*(26+1)/5=99.5328MHz.
Hoặc nếu ta chọn MUL=72, DIV=14 thì:
PLLCLK=MainClock*(MUL+1)/DIV=18.432MHz*(72+1)/14=96.109MHz.
Startup time cho PLL tùy thuộc vào mạch lọc và được tính toán bởi ATMEL, chương trình tính toán có thể down load tại đây:
[You must be registered and logged in to see this link.]Chép file: ATMEL_PLL_LFT_Filter_CALCULATOR_AT91_2v91.zip
Sử dụng chương trình này tôi tính ra được với:
R1=1500
C1=10n
C2=1n
=> startup time=0.515ms=PLLCOUNT*tSLCK Chọn tSLCK=23.8us như tính toán ở trên ta được PLLCOUNT=21.6.
Trong chương trình Assembler người ta chọn PLLCOUNT=28 (có lẽ mạch lọc của họ khác).
Dùng C:
pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 14) |
(AT91C_CKGR_PLLCOUNT & (10<<
) |
(AT91C_CKGR_MUL & (72<<16)));
// Wait the startup time (until PMC Status register LOCK bit is set)
while(!(pPMC->PMC_SR & AT91C_PMC_LOCK));
Dùng assembler:
LDR R1,=AT91SAM7SXXX_PMC_CKGR_PLLR ; //PHASE LOCKED LOOP REGISTER
LDR R2,=001A1C05 ; //OUT=0;DIV=5;PLLCOUNT=1CH;MUL=1AH;USBDIV=0.
STR R2,[R1,#0] ;APPLY USER SETTING
PLL_WAIT: LDR R1,[R0,#0] ;LOAD CURRENT STATUS
LSL R1,R1,#29 ;CHECK BIT 2//BIT LOCK// PLL is locked if SET
BPL PLL_WAIT ;WAIT FOR PLL STABLE
Cuối cùng là chọn Master clock cho hệ thống. Ở đây ta chọn Master clock là PLL clock/2=99.5328MHz/2=49.7664MHz.
Dùng C:
pPMC->PMC_MCKR = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_2;
Dùng Assembler
LDR R0,=AT91SAM7SXXX_PMC_MCKR ;// MASTER CLOCK REGISTER
MOV R1,#7 ;SETTING: PLL Clock is selected|Selected clock divided by 2
STR R1,[R0,#0] ;APPLY SETTING
Và màn hình tính thời gian khởi động PLL như sau:
Sao không post hình được nhỉ:
Viet Vinh
29-06-10, 16:02
(tiếp theo)
Công việc còn lại là cung cấp địa chỉ các hàm interrupt cho bộ Advanced Interrupt controller. Do ở chương trình nháy LED ta không dùng interrupt nên ta dùng các hàm mặc định.
Dùng C:
// Set up the default interrupts handler vectors
AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler;
for (i=1;i < 31; i++)
{
AT91C_BASE_AIC->AIC_SVR[i] = (int) AT91F_Default_IRQ_handler;
}
AT91C_BASE_AIC->AIC_SPU = (int) AT91F_Spurious_handler;
Dùng Assembler:
LDR R0,=AT91SAM7SXXX_AIC_SVR0 ;//Source Vector Register 0
LDR R1,=INT0 ; LOAD USER SETTING OF INTERRUPT HANDLER ADDRESS
STR R1,[R0,#0] ;APPLY SETTING
MOV R0,#1 ;PREPARE OFFSET
LDR R2,=AT91SAM7SXXX_AIC_SVR0 ; //Source Vector Register 0
LDR R3,=INT1_31 ;
INT_SET: LSL R1,R0,#2 ;OFFSET*=4
STR R3,[R2,R1] ;SET AT91SAM7SXXX_AIC_SVR0+4*R0 //Source Vector Register 1->31
ADD R0,R0,#1 ;PREPARE THE NEXT INTERRUPT
CMP R0,#31 ;LIMIT TO 31 REMAIN INTERRUPTS
BLT INT_SET ;DO SETTING FOR THE REMAIN 1->31INTERRUPTS
LDR R0,=AT91SAM7SXXX_AIC_SPU ;//Spurious Interrupt Vector Register
LDR R1,=INT_SPURIOUS ;//SPURIOUS INTERRUPT HANDLER ADDRESS
STR R1,[R0,#0] ;APPLY SETTING
================================================== ====================
Bây giờ ta xem xét tới Hàm Main
Trước tiên là tạo 2 Macro
#define BIT18 0x00040000
#define BIT17 0x00020000
Bởi vì 32 đường I/O sẽ được chỉ định cho thanh ghi 32 bit trong Bộ điều khiển I/O nên bít 0 tương ứng với PIO0, bit 1: PIO1, bít 2: PIO2 và cứ thế bit 31: PIO31.
Như vậy: 0x00020000 nghĩa là bit thứ 17 ở mức 1, 0x00040000: bit thứ 18 ở mức 1.
Do trong hàm main có sử dụng hàm delay nên ta xét hàm delay này:
Hàm delay ở đây là một hàm bẫy luồng xử lý vào một cái vòng luẩn quẩn trong suốt thời gian chờ. Đây là một hàm kém hiệu quả (lãng phí tài nguyên hệ thống) nhưng đơn giản.
Các hàm delay khác cho phép luồng xử lý tiếp tục làm các việc khác trong thời gian chờ, các hàm này tương đối phức tạp. Ta có thể dùng timer, nhưng trong chương trình nháy LED này, điều đó là không cần thiết.
Dùng C:
void Delay (unsigned long a) { while (--a!=0); }
Dùng ASSEMBLER:
Trước khi gọi hàm này, ta phải nạp giá trị delay vào R0.
DELAY: SUB R0,R0,#1
BNE $
ADD SP,#0 ;NOP
BX LR ;RETURN FROM DELAY
Trước khi trở lại hàm Main ta cũng nên xem qua địa chỉ của các thanh ghi sẽ dùng
#define AT91C_ID_PIOA ((unsigned int) 2) // Parallel IO Controller
#define AT91C_BASE_SYSC ((AT91PS_SYSC) 0xFFFFF000) // (SYSC) Base Address
#define AT91C_BASE_PIOA ((AT91PS_PIO) 0xFFFFF400) // (PIOA) Base Address
#define AT91C_BASE_PMC ((AT91PS_PMC) 0xFFFFFC00) // (PMC) Base Address
Khi khởi động ngoại vi, trước tiên ta phải khởi động Master clock cho ngoại vi mình muốn dùng. Cụ thể ở đây ta xài ngoại vi nhóm A.
Trong C trước tiên ta phải khởi tạo các con trỏ đến ngoại vi A (PIOA) và 1 con trỏ trỏ tới bộ điều khiển nguồn cung cấp (Power management controller) như sau:
AT91PS_PIO p_pPio = AT91C_BASE_PIOA;
AT91PS_PMC p_pPMC = AT91C_BASE_PMC;
AT91PS_PIO m_pPio = AT91C_BASE_PIOA;
Sau đó cấp xung nhịp cho thiết bị này:
Dùng C:
p_pPMC->PMC_PCER = 1 << AT91C_ID_PIOA;
Dùng ASSEMBLER:
LDR R0,AT91SAM7SXXX_PMC_PCER ;//PERIPHERAL CLOCK ENABLE REGISTER
MOV R1,#4 ;PID2 SET==Peripheral Clock 2 Enable. For AT91SAM7SXXX, PID2=Parallel I/O Controller A (PIOA)
STR R1,[R0,#0] ;APPLY SETTING
Bạn cũng có thể cân nhắc xem có nên dùng nút RESET bên ngoài hay không.
Dùng C:
AT91PS_SYSC pSysc=AT91C_BASE_SYSC;
pSysc->SYSC_RSTC_RMR |= 0xA5000001;// enable user reset
Dùng ASSEMBLER:
LDR R0,=AT91SAM7SXXX_RSTC_MR
LDR R1,[R0,#0] ;Đọc nội dung thanh ghi MODE
LDR R2,=0xA5000001 ;Thêm vào cài đặt của người dùng
ORR R2,R1 ;//A5H=WRITE_PASSWORD;01=Sử dụng RESET bên ngoài.
STR R2,[R0,#0] ;Cập nhật giá trị cài đặt
Việc kế tiếp là bạn phải kích hoạt PIO17 và PIO18 theo thủ tục của ATMEL
Dùng C:
p_pPio->PIO_PER |= BIT18 | BIT17 ; //Sử dụng PIO17 và PIO18
p_pPio->PIO_OER |= BIT18 | BIT17 ; //Chọn các PIO này là ngõ ra
p_pPio->PIO_SODR |= BIT18 | BIT17; //Set giá trị của 2 ngõ này lên 1
Dùng ASSEMBLER:
MOV R0,#C0H
LSL R0,R0,#11 ;Làm việc với PIO 17,18
LDR R1,=AT91SAM7SXXX_PIOA_PIO_PER ;//PIO ENABLE REGISTER
LDR R2,[R1,#0] ;Đọc giá trị cài đặt hiện tại
ORR R2,R0 ;Thêm vào những cài đặt mới cụ thể là cho phép PIO 17,18
STR R2,[R1,#0] ;Cập nhật các cài đặt
LDR R1,=PIO_OER
LDR R2,[R1,#0] ;Đọc giá trị cài đặt hiện tại
ORR R2,R0 ;Thêm vào những cài đặt mới cụ thể là xem PIO 17,18 như các ngõ ra
STR R2,[R1,#0] ;Cập nhật các cài đặt
LDR R4,=PIO_SODR
LDR R1[R4,#0] ;Đọc giá trị cài đặt hiện tại
ORR R0,R1 ;Thêm vào những cài đặt mới cụ thể là set PIO 17,18 lên mức 1
STR R0,[R4,#0] ;Cập nhật các cài đặt
Và cuối cùng là nháy LED. Nháy LED rất đơn giản lúc đầu ta bật LED17 và tắt LED18 sau một thời gian định sẵn ta làm ngược lại
Dùng C:
while (1) {
m_pPio->PIO_CODR = BIT18; //set reg to 0 (led2 on)
m_pPio->PIO_SODR = BIT17; //set reg to 1 (led1 off)
Delay(800000); //simple delay
m_pPio->PIO_CODR = BIT17; //set reg to 0 (led1 on)
m_pPio->PIO_SODR = BIT18; //set reg to 1 (led2 off)
Delay(800000); //simple delay
}
Dùng ASSEMBLER:
MOV R5,#80H
LSL R5,R5,#10 ;Làm việc với PIO17
LDR R6,PIO_CODR ;Địa chỉ thanh ghi Clear
LSL R7,R5,#1 ;Làm việc với PIO 18
LOOP: STR R7,[R6,#0] ;Tắt LED ở PIO18
STR R5,[R4,#0] ;Bật LED ở PIO17
LDR R0,TimeDelay ;Cài đặt thời gian trễ 000C3500H (=800000)
BL DELAY
STR R5,[R6,#0] ;Tắt LED ở PIO17
STR R7,[R4,#0] ;Bật LED ở PIO18
LDR R0,TimeDelay ;Cài đặt thời gian trễ 000C3500H (=800000)
BL DELAY
B LOOP
Đó, thật là đơn giản phải không các bạn. Qua các ví dụ này các bạn có thể hình dung được sơ lược về kiến trúc ARM, cách khởi động ARM và làm một chương trình nhỏ.
Tuy rằng nó chưa giúp được gì cho các bạn, nhưng các bạn cũng đã làm quen với nó rồi đó.
Hy vọng là chúng ta sẽ mau chóng làm chủ được con ARM này để ngành điện tử Việt Nam có thể phát triển mạnh.
Cảm ơn các bạn đã theo dõi, cảm ơn các mod đã sắp xếp lại và cuối cùng là cảm ơn DTVN. Tôi xin dừng lại ở đây.
***