Mục lục:

Hướng dẫn lắp ráp AVR 3: 9 bước
Hướng dẫn lắp ráp AVR 3: 9 bước

Video: Hướng dẫn lắp ráp AVR 3: 9 bước

Video: Hướng dẫn lắp ráp AVR 3: 9 bước
Video: Cách lắp bo avr ổn định điện áp máy phát an toàn cho thiết bị không cháy - lh 0982871662 2024, Tháng bảy
Anonim
Hướng dẫn lắp ráp AVR 3
Hướng dẫn lắp ráp AVR 3

Chào mừng đến với hướng dẫn số 3!

Trước khi chúng ta bắt đầu, tôi muốn đưa ra một quan điểm triết học. Đừng ngại thử nghiệm với các mạch và mã mà chúng tôi đang xây dựng trong các hướng dẫn này. Thay đổi các dây xung quanh, thêm các thành phần mới, lấy các thành phần ra, thay đổi các dòng mã, thêm các dòng mới, xóa các dòng và xem điều gì sẽ xảy ra! Rất khó để phá vỡ bất cứ thứ gì và nếu bạn làm vậy, ai quan tâm? Không có thứ gì chúng tôi đang sử dụng, kể cả bộ vi điều khiển, đều rất đắt tiền và việc xem mọi thứ có thể hỏng hóc như thế nào là điều cần thiết. Bạn không chỉ tìm ra những gì không nên làm trong lần tới mà quan trọng hơn, bạn sẽ biết tại sao không nên làm điều đó. Nếu bạn cũng giống như tôi, khi bạn còn là một đứa trẻ và bạn có một món đồ chơi mới, không lâu lắm bạn đã có nó thành từng mảnh để xem điều gì đã khiến nó trở nên đáng chú ý? Đôi khi đồ chơi bị hư hỏng không thể sửa chữa được nhưng không có vấn đề gì lớn. Cho phép một đứa trẻ khám phá tính tò mò của mình thậm chí đến mức đồ chơi bị hỏng là điều biến trẻ thành một nhà khoa học hoặc một kỹ sư thay vì một người rửa chén.

Hôm nay chúng ta sẽ đi dây một mạch rất đơn giản và sau đó sẽ đi sâu vào lý thuyết một chút. Xin lỗi về điều này, nhưng chúng tôi cần các công cụ! Tôi hứa chúng ta sẽ bù đắp điều này trong hướng dẫn 4, nơi chúng ta sẽ thực hiện một số xây dựng mạch nghiêm túc hơn và kết quả sẽ khá tuyệt. Tuy nhiên, cách bạn cần thực hiện tất cả các hướng dẫn này là một cách rất chậm rãi, mang tính chiêm nghiệm. Nếu bạn chỉ cày qua, xây dựng mạch, sao chép và dán mã, và chạy nó thì chắc chắn, nó sẽ hoạt động, nhưng bạn sẽ không học được gì. Bạn cần phải suy nghĩ về từng dòng. Tạm ngừng. Thí nghiệm. Phát minh. Nếu bạn làm theo cách đó thì vào cuối phần hướng dẫn thứ 5, bạn sẽ không cần phải xây dựng những thứ hay ho và không cần phải kèm cặp thêm. Nếu không, bạn chỉ đơn giản là xem hơn là học hỏi và sáng tạo.

Trong mọi trường hợp, đủ triết lý, chúng ta hãy bắt đầu!

Trong hướng dẫn này, bạn sẽ cần:

  1. bảng tạo mẫu của bạn
  2. một đèn LED
  3. kết nối dây
  4. một điện trở khoảng 220 đến 330 ohms
  5. Hướng dẫn sử dụng Bộ hướng dẫn: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Datasheet: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. một bộ dao động tinh thể khác (tùy chọn)

Đây là liên kết đến bộ sưu tập đầy đủ các hướng dẫn:

Bước 1: Xây dựng mạch

Cấu tạo mạch
Cấu tạo mạch

Mạch trong hướng dẫn này cực kỳ đơn giản. Về cơ bản, chúng ta sẽ viết chương trình "flash" vì vậy tất cả những gì chúng ta cần là những thứ sau.

Kết nối đèn LED với PD4, sau đó với điện trở 330 ohm, sau đó nối đất. I E.

PD4 - LED - R (330) - GND

Và đó là nó!

Mặc dù vậy, lý thuyết sẽ trở nên khó khăn…

Bước 2: Tại sao Chúng ta Cần Nhận xét và Tệp M328Pdef.inc?

Tôi nghĩ chúng ta nên bắt đầu bằng cách chỉ ra lý do tại sao tệp bao gồm và các nhận xét lại hữu ích. Không có cái nào trong số chúng thực sự cần thiết và bạn có thể viết, lắp ráp và tải lên mã theo cách tương tự mà không có chúng và nó sẽ chạy hoàn toàn tốt (mặc dù nếu không có tệp bao gồm, bạn có thể nhận được một số khiếu nại từ trình biên dịch - nhưng không có lỗi)

Đây là mã chúng ta sẽ viết ngày hôm nay, ngoại trừ việc tôi đã xóa các nhận xét và tệp bao gồm:

.device ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 out 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 out 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0x04 b: sbi 0x0b, 0x04 b: sbi 0x0b cbi 0x0b, 0x04 rcall c rjmp bc: clr r17 d: cpi r17, 0x1e brne d ret e: inc r17 cpi r17, 0x3d brne PC + 2 clr r17 reti

khá đơn giản phải không? Haha. Nếu bạn đã tập hợp và tải lên tệp này, bạn sẽ làm cho đèn LED nhấp nháy với tốc độ 1 lần nhấp nháy mỗi giây với thời gian nhấp nháy kéo dài 1/2 giây và thời gian tạm dừng giữa các lần nhấp nháy kéo dài 1/2 giây.

Tuy nhiên, nhìn vào mã này khó có thể khai sáng. Nếu bạn viết mã như thế này và bạn muốn sửa đổi nó hoặc sử dụng lại nó trong tương lai, bạn sẽ gặp khó khăn.

Vì vậy, chúng ta hãy đưa các nhận xét và đưa tệp vào lại để chúng ta có thể hiểu được điều đó.

Bước 3: Blink.asm

Đây là đoạn mã mà chúng ta sẽ thảo luận hôm nay:

;************************************

; được viết bởi: 1o_o7; ngày:; phiên bản: 1.0; tệp được lưu dưới dạng: flash.asm; cho AVR: atmega328p; tần số xung nhịp: 16MHz (tùy chọn); ************************************; Chương trình funcion: ---------------------; đếm giây bằng cách nhấp nháy đèn LED;; PD4 - LED - R (330 ohm) - GND;; --------------------------------------.nolist.bao gồm "./m328Pdef.inc".list; ==============; Các khai báo:.def temp = r16.def overflow = r17.org 0x0000; bộ nhớ (PC) vị trí của trình xử lý đặt lại rjmp Đặt lại; jmp tốn 2 chu kỳ cpu và rjmp chỉ tốn 1; vì vậy trừ khi bạn cần phải nhảy nhiều hơn 8k byte; bạn chỉ cần rjmp. Do đó, một số bộ vi điều khiển chỉ; có rjmp và không có jmp.org 0x0020; vị trí bộ nhớ của bộ xử lý tràn Timer0 rjmp pour_handler; vào đây nếu xảy ra ngắt tràn timer0; ============ Đặt lại: ldi temp, 0b00000101 out TCCR0B, temp; đặt Bits bộ chọn đồng hồ CS00, CS01, CS02 thành 101; điều này đặt Bộ đếm thời gian0, TCNT0 ở chế độ FCPU / 1024; vì vậy nó đánh dấu vào tần số CPU / 1024 ldi temp, 0b00000001 sts TIMSK0, temp; đặt bit Kích hoạt ngắt dòng tràn bộ hẹn giờ (TOIE0); của Thanh ghi mặt nạ ngắt bộ định thời (TIMSK0) sei; cho phép ngắt toàn cục - tương đương với "sbi SREG, I" clr temp out TCNT0, temp; khởi tạo Timer / Counter thành 0 sbi DDRD, 4; đặt PD4 thành đầu ra; ======================; Nội dung chính của chương trình: nháy: sbi PORTD, 4; bật đèn LED trên PD4 rcall delay; độ trễ sẽ là 1/2 giây cbi PORTD, 4; tắt đèn LED trên PD4 rcall delay; độ trễ sẽ là 1/2 giây rjmp nhấp nháy; vòng lặp trở lại độ trễ bắt đầu: clr tràn; đặt số lượng tràn thành 0 sec_count: tràn cpi, 30; so sánh số lần tràn và 30 brne sec_count; nhánh để quay lại sec_count nếu không bằng ret; nếu 30 lần tràn đã xảy ra, trả về nháy mắt tràn_handler: inc tràn; thêm 1 vào biến tràn cpi tràn, 61; so sánh với 61 brne PC + 2; Bộ đếm chương trình + 2 (bỏ qua dòng tiếp theo) nếu không bằng tràn clr; nếu 61 lần tràn xảy ra, hãy đặt lại bộ đếm về 0; trở lại sau khi gián đoạn

Như bạn có thể thấy, bình luận của tôi bây giờ ngắn gọn hơn một chút. Khi chúng ta biết các lệnh trong tập lệnh là gì, chúng ta không cần giải thích điều đó trong các bình luận. Chúng tôi chỉ cần giải thích những gì đang diễn ra theo quan điểm của chương trình.

Chúng ta sẽ thảo luận về những gì mà tất cả những điều này thực hiện từng phần một, nhưng trước tiên chúng ta hãy cố gắng có được một cái nhìn toàn cầu. Phần chính của chương trình hoạt động như sau.

Đầu tiên, chúng tôi đặt bit 4 của PORTD với "sbi PORTD, 4", điều này sẽ gửi 1 đến PD4, đặt điện áp thành 5V trên chân đó. Điều này sẽ bật đèn LED. Sau đó, chúng tôi chuyển đến chương trình con "delay" tính ra 1/2 giây (chúng tôi sẽ giải thích cách nó thực hiện điều này sau). Sau đó, chúng tôi quay lại nhấp nháy và xóa bit 4 trên PORTD đặt PD4 thành 0V và do đó tắt đèn LED. Sau đó, chúng tôi trì hoãn thêm 1/2 giây, và sau đó quay trở lại điểm bắt đầu của nhấp nháy một lần nữa với "rjmp nháy".

Bạn nên chạy mã này và thấy rằng nó thực hiện những gì cần thiết.

Và bạn có nó rồi đấy! Đó là tất cả những gì mã này thực hiện về mặt vật lý. Cơ chế bên trong của những gì bộ vi điều khiển đang làm có liên quan nhiều hơn một chút và đó là lý do tại sao chúng tôi thực hiện hướng dẫn này. Vì vậy, chúng ta hãy thảo luận lần lượt từng phần.

Bước 4:.org Assembler Directives

Chúng ta đã biết các chỉ thị trình hợp dịch.nolist,.list,.include và.def làm gì từ các hướng dẫn trước của chúng tôi, vì vậy trước tiên hãy xem 4 dòng mã sau đó:

.org 0x0000

jmp Đặt lại.org 0x0020 jmp tràn_handler

Câu lệnh.org cho trình hợp dịch biết vị trí trong "Bộ nhớ chương trình" để đặt câu lệnh tiếp theo. Khi chương trình của bạn thực thi, "Bộ đếm chương trình" (viết tắt là PC) chứa địa chỉ của dòng hiện tại đang được thực thi. Vì vậy, trong trường hợp này khi PC ở 0x0000, nó sẽ thấy lệnh "jmp Reset" nằm trong vị trí bộ nhớ đó. Lý do chúng tôi muốn đặt jmp Reset ở vị trí đó là vì khi chương trình bắt đầu, hoặc chip được đặt lại, PC sẽ bắt đầu thực thi mã tại vị trí này. Vì vậy, như chúng ta thấy, chúng ta vừa bảo nó lập tức "nhảy" sang phần có nhãn "Đặt lại". Tại sao chúng tôi làm điều đó? Điều đó có nghĩa là hai dòng cuối cùng ở trên chỉ bị bỏ qua! Tại sao?

Đó là nơi mọi thứ trở nên thú vị. Bây giờ bạn sẽ phải mở trình xem pdf với biểu dữ liệu ATmega328p đầy đủ mà tôi đã chỉ ra trên trang đầu tiên của hướng dẫn này (đó là lý do tại sao nó là mục 4 trong phần "bạn sẽ cần"). Nếu màn hình của bạn quá nhỏ hoặc bạn đã mở quá nhiều cửa sổ (như trường hợp của tôi), bạn có thể làm theo những gì tôi làm và đặt nó trên Ereader hoặc điện thoại Android của bạn. Bạn sẽ sử dụng nó mọi lúc nếu bạn định viết mã lắp ráp. Điều thú vị là tất cả các bộ vi điều khiển đều được tổ chức theo những cách rất giống nhau và vì vậy một khi bạn đã quen với việc đọc các biểu dữ liệu và mã hóa từ chúng, bạn sẽ thấy việc làm tương tự đối với một bộ vi điều khiển khác là rất nhỏ. Vì vậy, chúng tôi thực sự đang học cách sử dụng tất cả các bộ vi điều khiển theo một nghĩa nào đó chứ không chỉ atmega328p.

Được rồi, hãy chuyển sang trang 18 trong biểu dữ liệu và xem Hình 8-2.

Đây là cách bộ nhớ chương trình trong vi điều khiển được thiết lập. Bạn có thể thấy rằng nó bắt đầu bằng địa chỉ 0x0000 và được tách thành hai phần; một phần flash ứng dụng và một phần flash khởi động. Nếu bạn tham khảo sơ qua trang 277 bảng 27-14, bạn sẽ thấy phần flash ứng dụng chiếm các vị trí từ 0x0000 đến 0x37FF và phần flash khởi động chiếm các vị trí còn lại từ 0x3800 đến 0x3FFF.

Bài tập 1: Có bao nhiêu vị trí trong bộ nhớ Chương trình? I E. chuyển 3FFF sang thập phân và thêm 1 vì chúng ta bắt đầu đếm ở 0. Vì mỗi vị trí bộ nhớ rộng 16 bit (hoặc 2 byte) nên tổng số byte bộ nhớ là bao nhiêu? Bây giờ chuyển đổi giá trị này thành kilobyte, hãy nhớ rằng có 2 ^ 10 = 1024 byte trong một kilobyte. Phần flash khởi động đi từ 0x3800 đến 0x37FF, đây là bao nhiêu kilobyte? Chúng ta sử dụng bao nhiêu kilobyte bộ nhớ để lưu trữ chương trình của mình? Nói cách khác, chương trình của chúng ta có thể lớn đến mức nào? Cuối cùng, chúng ta có thể có bao nhiêu dòng mã?

Được rồi, bây giờ chúng ta đã biết tất cả về cách tổ chức bộ nhớ chương trình flash, hãy tiếp tục thảo luận về các câu lệnh.org. Chúng tôi thấy rằng vị trí bộ nhớ đầu tiên 0x0000 chứa hướng dẫn của chúng tôi để chuyển đến phần của chúng tôi mà chúng tôi đã gắn nhãn Đặt lại. Bây giờ chúng ta thấy những gì câu lệnh ".org 0x0020" làm. Nó nói rằng chúng ta muốn lệnh trên dòng tiếp theo được đặt ở vị trí bộ nhớ 0x0020. Hướng dẫn mà chúng tôi đã đặt ở đó là một bước nhảy đến một phần trong mã của chúng tôi mà chúng tôi đã gắn nhãn "flow_handler "… bây giờ tại sao chúng ta yêu cầu bước nhảy này được đặt ở vị trí bộ nhớ 0x0020? Để tìm hiểu, chúng ta lật sang trang 65 trong biểu dữ liệu và xem Bảng 12-6.

Bảng 12-6 là một bảng "Reset và Vectơ ngắt" và nó hiển thị chính xác nơi PC sẽ đi khi nó nhận được "ngắt". Ví dụ: nếu bạn nhìn vào Vectơ số 1. "Nguồn" của ngắt là "ĐẶT LẠI" được định nghĩa là "Pin ngoài, Đặt lại nguồn bật, Đặt lại Brown-out, và đặt lại hệ thống Cơ quan giám sát", nếu có những điều đó xảy ra với bộ vi điều khiển của chúng ta, PC sẽ bắt đầu thực thi chương trình của chúng ta tại vị trí bộ nhớ chương trình 0x0000. Vậy còn chỉ thị.org của chúng tôi thì sao? Chà, chúng tôi đã đặt một lệnh tại vị trí bộ nhớ 0x0020 và nếu bạn nhìn xuống bảng, bạn sẽ thấy rằng nếu xảy ra tràn Timer / Counter0 (đến từ TIMER0 OVF), nó sẽ thực thi bất cứ điều gì ở vị trí 0x0020. Vì vậy, bất cứ khi nào điều đó xảy ra, PC sẽ nhảy đến vị trí mà chúng tôi đã gắn nhãn là "" tràn_bộ xử lý. Tuyệt vời phải không? Bạn sẽ thấy trong một phút tại sao chúng tôi làm điều này, nhưng trước tiên hãy kết thúc bước này của hướng dẫn với một bên.

Nếu chúng ta muốn làm cho mã của mình gọn gàng và ngăn nắp hơn, chúng ta thực sự nên thay thế 4 dòng mà chúng ta hiện đang thảo luận bằng những dòng sau (xem trang 66):

.org 0x0000

rjmp Đặt lại; PC = 0x0000 reti; PC = 0x0002 reti; PC = 0x0004 reti; PC = 0x0006 reti; PC = 0x0008 reti; PC = 0x000A… nghỉ hưu; PC = 0x001E jmp tràn_handler: PC = 0x0020 reti: PC = 0x0022… reti; PC = 0x0030 về hưu; PC = 0x0032

Vì vậy, nếu một ngắt nhất định xảy ra, nó sẽ chỉ "nghỉ hưu" có nghĩa là "quay trở lại từ ngắt" và không có gì khác xảy ra. Nhưng nếu chúng ta không bao giờ "Bật" những ngắt khác nhau này, thì chúng sẽ không được sử dụng và chúng ta có thể đặt mã chương trình vào những chỗ này. Trong chương trình "flash.asm" hiện tại của chúng tôi, chúng tôi sẽ chỉ kích hoạt ngắt tràn timer0 (và tất nhiên là ngắt đặt lại luôn được kích hoạt) và vì vậy chúng tôi sẽ không bận tâm đến các ngắt khác.

Làm thế nào để chúng ta "kích hoạt" ngắt tràn timer0 sau đó? … Đó là chủ đề của bước tiếp theo của chúng tôi trong hướng dẫn này.

Bước 5: Hẹn giờ / Bộ đếm 0

Hẹn giờ / Bộ đếm 0
Hẹn giờ / Bộ đếm 0

Hãy nhìn vào hình ảnh trên. Đây là quá trình ra quyết định của "PC" khi một số tác động từ bên ngoài "làm gián đoạn" luồng chương trình của chúng ta. Điều đầu tiên nó làm khi nhận được tín hiệu từ bên ngoài rằng một ngắt đã xảy ra là nó sẽ kiểm tra xem chúng ta đã đặt bit "cho phép ngắt" cho loại ngắt đó hay chưa. Nếu chúng ta chưa có, thì nó sẽ tiếp tục thực thi dòng mã tiếp theo của chúng ta. Nếu chúng ta đã đặt bit cho phép ngắt cụ thể đó (để có 1 ở vị trí bit đó thay vì 0) thì nó sẽ kiểm tra xem chúng ta đã bật "ngắt toàn cục" hay chưa, nếu chưa nó sẽ chuyển sang dòng tiếp theo mã và tiếp tục. Nếu chúng ta cũng đã kích hoạt ngắt toàn cục, thì nó sẽ đi đến vị trí Bộ nhớ chương trình của loại ngắt đó (như trong Bảng 12-6) và thực hiện bất kỳ lệnh nào chúng ta đã đặt ở đó. Vì vậy, hãy xem cách chúng tôi đã triển khai tất cả những điều này trong mã của chúng tôi.

Phần có nhãn Đặt lại trong mã của chúng tôi bắt đầu bằng hai dòng sau:

Cài lại:

nhiệt độ ldi, 0b00000101 ra TCCR0B, nhiệt độ

Như chúng ta đã biết, điều này tải vào nhiệt độ (tức là R16) số ngay sau đó, là 0b00000101. Sau đó, nó ghi số này ra thanh ghi có tên là TCCR0B bằng lệnh "out". Sổ đăng ký này là gì? Vâng, hãy chuyển sang trang 614 của biểu dữ liệu. Đây là ở giữa bảng tóm tắt tất cả các thanh ghi. Tại địa chỉ 0x25, bạn sẽ tìm thấy TCCR0B. (Bây giờ bạn đã biết dòng "out 0x25, r16" đến từ đâu trong phiên bản mã chưa được nhận xét của tôi). Chúng ta thấy trong đoạn mã ở trên rằng chúng ta đã đặt bit thứ 0 và bit thứ 2 và xóa tất cả phần còn lại. Bằng cách nhìn vào bảng, bạn có thể thấy rằng điều này có nghĩa là chúng tôi đã đặt CS00 và CS02. Bây giờ chúng ta hãy chuyển sang chương trong biểu dữ liệu có tên "Bộ định thời / Bộ đếm 8-bit với PWM". Đặc biệt, hãy chuyển đến trang 107 của chương đó. Bạn sẽ thấy mô tả tương tự về thanh ghi "Thanh ghi điều khiển bộ định thời / bộ đếm B" (TCCR0B) mà chúng ta vừa thấy trong bảng tóm tắt thanh ghi (vì vậy chúng ta có thể đến thẳng đây, nhưng tôi muốn bạn xem cách sử dụng bảng tóm tắt để tham khảo trong tương lai). Biểu dữ liệu tiếp tục đưa ra mô tả về từng bit trong thanh ghi đó và những gì chúng làm. Bây giờ chúng ta sẽ bỏ qua tất cả những điều đó và chuyển trang sang Bảng 15-9. Bảng này hiển thị "Mô tả bit chọn đồng hồ". Bây giờ hãy nhìn xuống bảng đó cho đến khi bạn tìm thấy dòng tương ứng với các bit mà chúng ta vừa đặt trong thanh ghi đó. Dòng cho biết "clk / 1024 (từ bộ cài sẵn)". Điều này có nghĩa là chúng ta muốn Timer / Counter0 (TCNT0) đánh dấu cùng với tốc độ là tần số CPU chia cho 1024. Vì chúng ta có bộ vi điều khiển được cung cấp bởi bộ dao động tinh thể 16MHz, có nghĩa là tốc độ mà CPU của chúng ta thực hiện các lệnh là 16 triệu hướng dẫn mỗi giây. Vì vậy, tỷ lệ mà bộ đếm TCNT0 của chúng tôi sẽ đánh dấu khi đó là 16 triệu / 1024 = 15625 lần mỗi giây (hãy thử nó với các bit chọn đồng hồ khác nhau và xem điều gì sẽ xảy ra - hãy nhớ triết lý của chúng tôi?). Hãy ghi nhớ con số 15625 trong tâm trí chúng ta sau này và chuyển sang hai dòng mã tiếp theo:

nhiệt độ ldi, 0b00000001

sts TIMSK0, nhiệt độ

Điều này đặt bit thứ 0 của thanh ghi được gọi là TIMSK0 và xóa tất cả phần còn lại. Nếu bạn xem trang 109 trong biểu dữ liệu, bạn sẽ thấy rằng TIMSK0 là viết tắt của "Timer / Counter Interrupt Mask Register 0" và mã của chúng tôi đã đặt bit thứ 0 được đặt tên là TOIE0, viết tắt của "Timer / Counter0 Overflow Interrupt Enable" … Ở đó! Bây giờ bạn thấy những gì đây là tất cả về. Bây giờ chúng tôi có "bộ bit cho phép ngắt" như chúng tôi muốn từ quyết định đầu tiên trong hình của chúng tôi ở trên cùng. Vì vậy, bây giờ tất cả những gì chúng ta phải làm là kích hoạt "ngắt toàn cục" và chương trình của chúng ta sẽ có thể đáp ứng với những loại ngắt này. Chúng tôi sẽ kích hoạt ngắt toàn cục trong thời gian ngắn, nhưng trước khi chúng tôi làm điều đó, bạn có thể đã bị nhầm lẫn bởi điều gì đó.. tại sao tôi lại sử dụng lệnh "sts" để sao chép vào thanh ghi TIMSK0 thay vì "out" thông thường?

Bất cứ khi nào bạn thấy tôi sử dụng một hướng dẫn mà bạn chưa thấy trước đây, điều đầu tiên bạn nên làm là lật đến trang 616 trong biểu dữ liệu. Đây là "Tóm tắt Bộ Hướng dẫn". Bây giờ hãy tìm hướng dẫn "STS" mà tôi đã sử dụng. Nó nói rằng nó lấy một số từ thanh ghi R (chúng tôi đã sử dụng R16) và "Lưu trữ trực tiếp đến SRAM" vị trí k (trong trường hợp của chúng tôi là do TIMSK0 đưa ra). Vậy tại sao chúng ta phải sử dụng "sts" cần 2 chu kỳ đồng hồ (xem cột cuối cùng trong bảng) để lưu trữ trong TIMSK0 và chúng tôi chỉ cần "out", tức chỉ mất một chu kỳ đồng hồ, để lưu trữ trong TCCR0B trước đây? Để trả lời câu hỏi này, chúng ta cần quay lại bảng tóm tắt thanh ghi của chúng ta ở trang 614. Bạn thấy rằng thanh ghi TCCR0B ở địa chỉ 0x25 nhưng cũng ở (0x45) đúng không? Điều này có nghĩa rằng nó là một thanh ghi trong SRAM, nhưng nó cũng là một loại thanh ghi nhất định được gọi là "cổng" (hoặc thanh ghi i / o). Nếu bạn nhìn vào bảng tóm tắt hướng dẫn bên cạnh lệnh "out", bạn sẽ thấy rằng nó lấy các giá trị từ "các thanh ghi đang làm việc" như R16 và gửi chúng đến một PORT. Vì vậy, chúng ta có thể sử dụng "out" khi ghi vào TCCR0B và lưu cho mình một chu kỳ xung nhịp. Nhưng bây giờ hãy tra cứu TIMSK0 trong bảng đăng ký. Bạn thấy rằng nó có địa chỉ 0x6e. Điều này nằm ngoài phạm vi của các cổng (chỉ là các vị trí 0x3F đầu tiên của SRAM) và vì vậy bạn phải quay lại sử dụng lệnh sts và mất hai chu kỳ xung nhịp CPU để thực hiện. Vui lòng đọc Chú thích 4 ở cuối bảng tóm tắt hướng dẫn trên trang 615 ngay bây giờ. Cũng lưu ý rằng tất cả các cổng đầu vào và đầu ra của chúng tôi, như PORTD đều nằm ở cuối bảng. Ví dụ: PD4 là bit 4 tại địa chỉ 0x0b (bây giờ bạn thấy tất cả nội dung 0x0b đến từ đâu trong mã chưa được nhận xét của tôi!).. được rồi, câu hỏi nhanh: bạn đã thay đổi "sts" thành "out" và xem điều gì xảy ra? Hãy nhớ triết lý của chúng tôi! phá vỡ nó! đừng chỉ dùng lời nói của tôi cho mọi thứ.

Được rồi, trước khi chúng ta tiếp tục, hãy chuyển sang trang 19 trong biểu dữ liệu trong một phút. Bạn thấy hình ảnh của bộ nhớ dữ liệu (SRAM). 32 thanh ghi đầu tiên trong SRAM (từ 0x0000 đến 0x001F) là "thanh ghi làm việc cho mục đích chung" R0 đến R31 mà chúng ta luôn sử dụng làm biến trong mã của chúng ta.64 thanh ghi tiếp theo là các cổng I / O lên đến 0x005f (tức là những cổng mà chúng ta đang đề cập đến có các địa chỉ không được đặt trong ngoặc vuông bên cạnh chúng trong bảng đăng ký mà chúng ta có thể sử dụng lệnh "out" thay vì "sts") phần tiếp theo của SRAM chứa tất cả các thanh ghi khác trong bảng tóm tắt đến địa chỉ 0x00FF, và cuối cùng phần còn lại là SRAM nội bộ. Bây giờ, hãy nhanh chóng chuyển sang trang 12 trong một giây. Ở đó, bạn thấy một bảng "thanh ghi làm việc cho mục đích chung" mà chúng tôi luôn sử dụng làm biến của mình. Bạn thấy vạch dày giữa các số R0 đến R15 và sau đó là R16 đến R31? Dòng đó là lý do tại sao chúng tôi luôn sử dụng R16 là dòng nhỏ nhất và tôi sẽ đi sâu hơn một chút trong hướng dẫn tiếp theo, nơi chúng tôi cũng sẽ cần ba thanh ghi địa chỉ gián tiếp 16 bit, X, Y và Z. Tôi sẽ không hãy làm điều đó ngay lập tức vì chúng tôi không cần nó bây giờ và chúng tôi đã sa lầy đủ ở đây.

Lật lại một trang đến trang 11 của biểu dữ liệu. Bạn sẽ thấy một sơ đồ của thanh ghi SREG ở trên cùng bên phải? Bạn thấy rằng bit 7 của thanh ghi đó được gọi là "I". Bây giờ hãy xuống trang và đọc phần mô tả của Bit 7…. vâng! Nó là bit Kích hoạt ngắt toàn cầu. Đó là những gì chúng ta cần thiết lập để chuyển qua quyết định thứ hai trong sơ đồ của chúng ta ở trên và cho phép ngắt tràn bộ đếm thời gian / bộ đếm trong chương trình của chúng ta. Vì vậy, dòng tiếp theo của chương trình của chúng ta nên đọc:

sbi SREG, tôi

đặt bit được gọi là "I" trong thanh ghi SREG. Tuy nhiên, thay vì điều này, chúng tôi đã sử dụng hướng dẫn

sei

thay thế. Bit này được đặt thường xuyên trong các chương trình nên họ chỉ tạo ra một cách đơn giản hơn để thực hiện.

Được chứ! Bây giờ chúng ta đã có sẵn các ngắt tràn để chạy để "jmp pour_handler" của chúng ta sẽ được thực thi bất cứ khi nào xảy ra.

Trước khi chúng ta tiếp tục, hãy xem nhanh thanh ghi SREG (Thanh ghi trạng thái) vì nó rất quan trọng. Đọc những gì mỗi lá cờ đại diện. Đặc biệt, nhiều hướng dẫn mà chúng tôi sử dụng sẽ đặt và kiểm tra các cờ này mọi lúc. Ví dụ, sau này chúng ta sẽ sử dụng lệnh "CPI" có nghĩa là "so sánh ngay lập tức". Hãy xem bảng tóm tắt hướng dẫn cho hướng dẫn này và để ý xem nó đặt bao nhiêu cờ trong cột "cờ". Đây là tất cả các cờ trong SREG và mã của chúng tôi sẽ thiết lập chúng và kiểm tra chúng liên tục. Bạn sẽ thấy các ví dụ ngay sau đây. Cuối cùng, bit cuối cùng của đoạn mã này là:

clr temp

ra TCNT0, DDRD tạm thời sbi, 4

Dòng cuối cùng ở đây là khá rõ ràng. Nó chỉ đặt bit thứ 4 của thanh ghi hướng dữ liệu cho PortD khiến PD4 là OUTPUT.

Cái đầu tiên đặt nhiệt độ biến bằng 0 và sau đó sao chép nó vào thanh ghi TCNT0. TCNT0 là Timer / Counter0 của chúng tôi. Điều này đặt nó thành 0. Ngay sau khi PC thực hiện dòng này, timer0 sẽ bắt đầu ở 0 và đếm với tốc độ 15625 lần mỗi giây. Vấn đề là: TCNT0 là một thanh ghi "8-bit" phải không? Vậy số lớn nhất mà thanh ghi 8-bit có thể chứa là bao nhiêu? Chà 0b11111111 là nó. Đây là số 0xFF. Đó là 255. Vì vậy, bạn thấy những gì sẽ xảy ra? Bộ đếm thời gian đang chạy theo tốc độ tăng 15625 lần một giây và mỗi khi đạt đến 255, nó "tràn" và quay trở lại 0 một lần nữa. Đồng thời khi nó quay trở lại 0, nó sẽ gửi tín hiệu Ngắt Tràn Bộ hẹn giờ. PC nhận được điều này và bạn biết nó làm gì bây giờ không? Chuẩn rồi. Nó đi đến vị trí Bộ nhớ chương trình 0x0020 và thực hiện lệnh mà nó tìm thấy ở đó.

Tuyệt vời! Nếu bạn vẫn ở bên tôi thì bạn là một siêu anh hùng không mệt mỏi! Cứ đi đi…

Bước 6: Xử lý tràn

Vì vậy, giả sử rằng thanh ghi timer / counter0 vừa bị tràn. Bây giờ chúng ta biết rằng chương trình nhận được một tín hiệu ngắt và thực thi 0x0020 để báo cho Bộ đếm chương trình, PC chuyển đến nhãn "tràn_handler", sau đây là mã chúng ta đã viết sau nhãn đó:

flow_handler:

inc tràn cpi tràn, 61 brne PC + 2 clr tràn reti

Điều đầu tiên nó làm là tăng biến "tràn" (là tên của chúng tôi cho thanh ghi làm việc mục đích chung R17) sau đó "so sánh" nội dung của các lỗi tràn với số 61. Cách mà lệnh cpi hoạt động là nó chỉ đơn giản là trừ hai số và nếu kết quả là 0, nó sẽ đặt cờ Z trong thanh ghi SREG (tôi đã nói với bạn rằng chúng ta sẽ thấy thanh ghi này mọi lúc). Nếu hai số bằng nhau thì cờ Z sẽ là số 1, nếu hai số không bằng nhau thì cờ Z sẽ là số 0.

Dòng tiếp theo ghi "brne PC + 2" có nghĩa là "chi nhánh nếu không bằng nhau". Về cơ bản, nó kiểm tra cờ Z trong SREG và nếu nó KHÔNG phải là một (tức là hai số không bằng nhau, nếu chúng bằng nhau, cờ 0 sẽ được đặt) thì PC sẽ chuyển sang PC + 2, nghĩa là nó sẽ bỏ qua bước tiếp theo dòng và đi thẳng đến "reti", trả về từ ngắt đến bất kỳ vị trí nào trong mã khi ngắt đến. Nếu lệnh brne tìm thấy 1 trong bit cờ 0, nó sẽ không phân nhánh và thay vào đó nó sẽ chỉ tiếp tục đến dòng tiếp theo sẽ tràn clr đặt lại nó về 0.

Kết quả ròng của tất cả những điều này là gì?

Chúng ta thấy rằng mỗi khi có một lần tràn bộ định thời, trình xử lý này sẽ tăng giá trị của "lần tràn" lên một. Vì vậy, biến "tràn" đang đếm số lần tràn khi chúng xảy ra. Bất cứ khi nào con số đạt đến 61, chúng tôi đặt lại nó về không.

Bây giờ tại sao trên thế giới chúng ta sẽ làm điều đó?

Hãy xem nào. Nhớ lại rằng tốc độ xung nhịp cho CPU của chúng tôi là 16MHz và chúng tôi đã "cài đặt trước" nó bằng cách sử dụng TCCR0B để bộ đếm thời gian chỉ đếm ở tốc độ 15625 đếm mỗi giây phải không? Và mỗi khi bộ đếm thời gian đạt đến con số 255, nó sẽ tràn. Vì vậy, điều đó có nghĩa là nó tràn 15625/256 = 61,04 lần mỗi giây. Chúng tôi đang theo dõi số lần tràn với biến "tràn" của chúng tôi và chúng tôi đang so sánh số đó với 61. Vì vậy, chúng tôi thấy rằng "tràn" sẽ bằng 61 một lần mỗi giây! Vì vậy, trình xử lý của chúng tôi sẽ đặt lại "tràn" về 0 một lần mỗi giây. Vì vậy, nếu chúng ta chỉ đơn giản theo dõi biến "tràn" và lưu ý mỗi khi nó đặt lại về 0, chúng ta sẽ đếm từng giây trong thời gian thực (Lưu ý rằng trong hướng dẫn tiếp theo, chúng tôi sẽ chỉ ra cách lấy chính xác hơn trễ tính bằng mili giây giống như cách mà quy trình "trì hoãn" Arduino hoạt động).

Bây giờ chúng tôi đã "xử lý" các ngắt tràn bộ định thời. Đảm bảo rằng bạn hiểu cách hoạt động của điều này và sau đó chuyển sang bước tiếp theo mà chúng tôi sử dụng thực tế này.

Bước 7: Trì hoãn

Bây giờ chúng ta đã thấy rằng quy trình xử lý ngắt tràn bộ định thời của chúng ta "" "tràn_sao" sẽ đặt biến "tràn" thành 0 một lần mỗi giây, chúng ta có thể sử dụng thực tế này để thiết kế một chương trình con "trì hoãn".

Hãy xem đoạn mã sau từ dưới sự chậm trễ của chúng tôi: nhãn

trì hoãn:

clr tràn sec_count: cpi tràn, 30 brne sec_count ret

Chúng tôi sẽ gọi chương trình con này bất cứ khi nào chúng tôi cần sự chậm trễ trong chương trình của mình. Cách thức hoạt động là đầu tiên nó đặt biến "tràn" thành 0. Sau đó, nó đi vào một khu vực có nhãn "sec_count" và so sánh các phần tràn với 30, nếu chúng không bằng nhau, nó sẽ phân nhánh trở lại nhãn sec_count và so sánh một lần nữa, và một lần nữa, v.v. cho đến khi chúng bằng nhau (hãy nhớ rằng toàn bộ thời gian điều này sẽ diễn ra trên trình xử lý ngắt bộ hẹn giờ của chúng ta đang tiếp tục tăng biến số tràn và do đó, nó sẽ thay đổi mỗi khi chúng ta đi vòng quanh đây. Khi số tràn cuối cùng bằng 30, nó sẽ ra khỏi vòng lặp và quay trở lại bất cứ nơi nào chúng ta gọi là delay: from. Kết quả ròng là trễ 1/2 giây

Bài tập 2: Thay đổi quy trìnhflow_handler thành như sau:

flow_handler:

inc tràn về hưu

và chạy chương trình. Có gì khác nhau không? Tại sao hoặc tại sao không?

Bước 8: Chớp mắt

Cuối cùng, hãy xem xét thói quen chớp mắt:

chớp mắt:

sbi PORTD, 4 rcall delay cbi PORTD, 4 rcall delay rjmp flash

Đầu tiên chúng ta bật PD4, sau đó chúng ta gọi chương trình con trì hoãn của mình. Chúng tôi sử dụng rcall để khi PC nhận được câu lệnh "ret", nó sẽ quay trở lại dòng sau rcall. Sau đó, quy trình trì hoãn sẽ trì hoãn 30 lần đếm trong biến tràn như chúng ta đã thấy và điều này gần chính xác là 1/2 giây, sau đó chúng ta tắt PD4, trì hoãn thêm 1/2 giây và sau đó quay lại từ đầu.

Kết quả thực là một đèn LED nhấp nháy!

Tôi nghĩ rằng bây giờ bạn sẽ đồng ý rằng "nháy mắt" có lẽ không phải là chương trình "chào thế giới" tốt nhất trong hợp ngữ.

Bài tập 3: Thay đổi các thông số khác nhau trong chương trình để đèn LED nhấp nháy với các tốc độ khác nhau như một giây hoặc 4 lần một giây, v.v. Bài tập 4: Thay đổi để đèn LED bật và tắt trong các khoảng thời gian khác nhau. Ví dụ bật trong 1/4 giây rồi tắt trong 2 giây hoặc tương tự như vậy. Bài tập 5: Thay đổi bit chọn đồng hồ TCCR0B thành 100 rồi tiếp tục đi lên bảng. Bài tập 6 (tùy chọn): Nếu bạn có một bộ dao động tinh thể khác, chẳng hạn như 4 MHz hoặc 13,5 MHz hoặc bất cứ điều gì, hãy thay đổi bộ dao động 16 MHz của bạn trên bảng mạch mới của bạn và xem điều đó ảnh hưởng như thế nào đến tốc độ nhấp nháy của đèn LED. Bây giờ bạn sẽ có thể thực hiện tính toán chính xác và dự đoán chính xác nó sẽ ảnh hưởng đến tỷ lệ như thế nào.

Bước 9: Kết luận

Đối với những người trong số các bạn đã làm được điều đó đến nay, Xin chúc mừng!

Tôi nhận ra rằng việc đọc và tra cứu khá khó khăn khi bạn đọc và tra cứu nhiều hơn là nối dây và thử nghiệm nhưng tôi hy vọng bạn đã học được những điều quan trọng sau:

  1. Cách hoạt động của bộ nhớ chương trình
  2. Cách SRAM hoạt động
  3. Cách tra cứu sổ đăng ký
  4. Cách tra cứu hướng dẫn và biết chúng làm gì
  5. Cách triển khai ngắt
  6. Cách CP thực thi mã, cách SREG hoạt động và điều gì xảy ra khi ngắt
  7. Làm thế nào để thực hiện các vòng lặp và nhảy và trả lại trong mã
  8. Việc đọc biểu dữ liệu quan trọng như thế nào!
  9. Làm thế nào một khi bạn biết cách thực hiện tất cả những điều này đối với vi điều khiển Atmega328p thì sẽ là một bước đi tương đối để tìm hiểu bất kỳ bộ điều khiển mới nào mà bạn quan tâm.
  10. Cách thay đổi thời gian CPU thành thời gian thực và sử dụng nó trong các thói quen trì hoãn.

Bây giờ chúng ta có rất nhiều lý thuyết giúp chúng ta có thể viết mã tốt hơn và kiểm soát những thứ phức tạp hơn. Vì vậy, hướng dẫn tiếp theo chúng tôi sẽ làm điều đó. Chúng tôi sẽ xây dựng một mạch phức tạp hơn, thú vị hơn và điều khiển nó theo những cách thú vị.

Bài tập 7: "Phá vỡ" mã theo nhiều cách khác nhau và xem điều gì sẽ xảy ra! Bé tò mò khoa học! Ai khác có thể rửa bát đúng không? Bài tập 8: Lắp ráp đoạn mã bằng cách sử dụng tùy chọn "-l" để tạo một tệp danh sách. I E. "avra -l nháy.lst nháy.asm" và xem tệp danh sách. Tín dụng bổ sung: Mã chưa nhận xét mà tôi đã đưa ra lúc đầu và mã đã nhận xét mà chúng ta sẽ thảo luận sau khác nhau! Có một dòng mã khác nhau. Bạn có thể tìm thấy nó không? Tại sao sự khác biệt đó không quan trọng?

Hy vọng bạn đã vui vẻ! Hẹn gặp lại lần sau…

Đề xuất: