10 4.104 8051 9 Acquy adc pic All datasheet ALTIUM Altium Designer AM-FM Arduino ARM ARM là gì Ấn Tượng Bản tin công nghệ Bản tin Thiết Bị Số Barobo bất động sản biến Binary Bit board lpc2378 Bộ chuyển đổi ADC Bộ Đếm Bộ điều khiển cửa cuốn Bộ Định Thời Buy Khóa Số Điện Tử Buy Mạch đếm sản phẩm Buy Mạch giao thông Buy Mạch nạp Buy Matrix Byte C cho AVR các hàm vào ra các loại lõi arm các mạch DAC cơ bản các ngắt trong pic Cách đọc điện trở Cách Đọc Giá Trị Điện Trở Cách đọc giá trị tụ điện Cách hàn linh kiện dán cách làm mạch khóa số cách tạo linh kiện dán cad/cam Cài Đặt cài đặt proteus 8 cảm biến Cấu Kiện Logic Khả Trình cấu tạo cấu trúc arm cấu trúc lệnh CCS Chân chân Transistor Chip Khả Trình chuyển đổi Chuyển đổi số tương tự Chuyển Đổi Tương Tự/Số - ADC Chuyển động số chương trình City Clip Điện Tử Code 8051 - ASM Code 8051 - C Code 8051-C code ASM code ASM mẫu 8086 Code AVR - C code C Code Lập Trình Code led sao băng code maupic code mẫu 8051 Code Mẫu 8086 Code Mẫu cho ARM - LPC1343 code mẫu pic Code PIC - C codemaupic Counter Cổng Vào Ra Cơ Bản Cuộn Cảm Cửa cuốn DA DAC Debug Decimal Delay8051 Dev-C++ Diode DIY Dò đường Do It Yourself doanh nghiệp Download DTMF Mobile đảo chiều động cơ Điện Trở Điện Tử Điện Tử Cơ Bản điều chế độ rộng xung điều chế xung PWM điều khiển bằng điện thoại Điều khiển cửa cuốn Điều khiển cửa cuốn bằng điện thoại điều khiển động cơ đo điện áp đo nhiệt độ đo nhiệt độ hiển thị lên lcd trên 8051 Đo Nhiệt Độ LM35 + LCD Đo Nhiệt Độ LM35 + Led 7 thanh đọc màu điện trở đồ chơi động cơ chân không Động cơ nhiên liệu Động cơ robo đồng hồ thời gian thực Ebook Đại Học ebook điện tử Ebook đồ án Ebook Tin Học Encoder Encoder là gì Full Giải Thuật Giải thuật PID Giáo Dục giao tiếp i2c pic 16f877a giao tiếp i2c pic16f877a với ic ds1307 giao tiếp máy tính qua rs232 Giao Tiếp Máy Tính VB6 giao tiếp rs232 giao tiếp spi giữa 2 pic giao tiếp spi trong pic Giáo Trình Điện Tử Giới thiệu 8051 Giới thiệu cơ bản GPIO Graphic Design hàm Hàn linh kiện dán Hexadecimal Hệ Hexa Hệ Nhị Phân Hệ Thập Lục Phân Hệ Thập Phân hiển thị lên lcd 16x2 Hoạt Động Học Học 8051 qua các ví dụ đơn giản Học ALtium Designer học AVR Học Corel Draw X3 Học Eagle HỌC LẬP TRÌNH 16F877A Học Lập Trình 8051 Học Lập Trình C Học Orcad Học Protues hoc-lam-robot-do-duong-qua-video Hồng ngoại hướng dẫn hướng dẫn Altium Designer hướng dẫn đo đồng hồ VOM hướng dẫn keil - C lập trình 8051 hướng dẫn làm led sao băng hướng dẫn làm led trái tim hướng dẫn lập trình ARM Hướng Dẫn Lập Trình ARM - LPC1343 hướng dẫn lập trình ARM-LPC2378 hướng dẫn lập trình CCS hướng dẫn lập trình PIC Hướng Dẫn Led Trái Tim hướng dẫn module sim548c hướng dẫn sử dụng keil hướng dẫn sử dụng proteus 8 Hyper Terminal hercules 3.2.4 I/O IC 555 IC 7447 IC 74HC151 IC 74HC154 IC 74HC245 IC 74HC595 IC 74LS138 IC DS1307 IC đồng hồ thời gian thực IC LM324 IC LM342 IC LM7805 IC số IC số opamp LM324 IC Thông Dụng IC555 Interrupt Keil 4 Full keil arm Keil C Keil uVision3 kế toán kiểm toán khái niệm Khái Niệm Cơ Bản Kho Vật Liệu khóa điện tử khóa số dùng 8051 khóa số dùng 89s52 Khóa Số Điện Tử khuếch đại kiểm tra Kinh doanh maketing kinh tế quản lí Kỹ Thuật Kỹ Thuật Vi Xử Lý làm mạch điện lý thú Làm quen AVR Lap Trinh Dieu Khien Robot Lập Trình lập trình 8051 Lập Trình AVR Lập Trình C lập trình c++ Lập Trình Led Quảng Cáo Lập Trình Nhúng Lập trình pic Lập trình Robot Lập Trình Vi Điều Khiển Lập Trình Với AVR Studio LCD 16x2 Lcd16x2 Led Clock Led Quay led sao băng led trai tim Led Trái Tim Lịch sử ra đời Linh Kiện Cơ Bản linh kiện điện tử Loa LPC 2378 LSB lý thú Mã AVR - C Mạch 7seg Mạch Amply.Mạch Loa Mạch Autorobo Mạch bảo vệ Mạch Cảm Biến mạch cảm ứng sờ tay Mạch Cầu H Mạch cube Mạch Đếm Sản Phẩm Mạch điện cơ bản Mạch điện hay Mạch Điện Ứng Dụng Mạch Điều khiển động cơ Mạch Động Cơ Mạch đồng hồ Mạch đồng hồ 4 led Mạch giao thông Mạch IC số Mạch in mạch khóa số mạch khuếch đại thuật toán mạch led chúc mừng năm mới mạch led đẹp Mạch Led đơn Mạch Led Quảng Cáo mach led trai tim mạch led trái tim Mạch Led Vumeter mạch lý thú Mạch Ma trận Phím Mạch Matrix Mạch nạp Mạch nguồn Mạch Nút Bấm mạch sóng rf mạch tăng áp Mạch thu phát Mạch tổ hợp MSI Mạch trái tim Mạch Vi điều khiển Microbicho module module GSM/GPS Module Sim548 Module Sim548 giao tiếp với vi điều khiển PIC Module Sim548C Mosfet Motor Mô Phỏng Phần Cứng Mô Tả Phần Cứng MSB mua led sao băng News Ngắt Ngắt Trong LPC23xx ngân hàng Ngôn Ngữ Ngôn Ngữ C Ngôn Ngữ Tự Học Lập Trình C Ngôn Ngữ VHDL Nguyên Lý nguyên lý ic 555 Nguyên Tắc nháy led Nhập môn C Nhỏ Gọn Nibble opamp People Phần Mềm phần mềm altium Designer Phần mềm điện tử Phần Mềm Điện Tử Phần Mềm Điện Tử Hay Phần Mềm Hay Phần Mềm Led Quảng Cáo phần mềm proteus 8 Phần mềm vi tính Phần Mền Phương pháp hàn linh kiện dán PIC pic16f877a Print Design Proteus Proteus 7.8 SP2 FULL PWM quà tặng bạn gái quà tặng độc đáo quản trị doanh nghiệp quản trị kinh doanh quét led 7 đoạn Relay robocon Robot ROBOT DÒ ĐƯỜNG rút gọn mạch logic tổ hợp Sach Dien Tu Sản Phẩm Thú Vị Sản Phẩm Thương Mại Sáng tạo Short Smart Home SMD sơ đồ nguyên lý spi Sports Sử Dụng Sử Dụng Đồng Hồ sử dụng đồng hồ VOM sử dụng ngắt trong pic sự khác nhau Sức mạnh số Tải tài chính tài chính doanh nghiệp tài chính ngân hàng Tài Khoản Chia Sẻ Tài Liệu Tài Liệu 8051 tài liệu avr Tài liệu Điện Tử Tài Liệu Pic Tài liệu robocon tài liệu về ngân hàng Tài Liệu Vi Điều Khiển tailieuvn Tạo cổng Com ảo Tạo cổng nối tiếp ảo tạo dự án trong keil arm Tạo Project trong Vi Xử Lý ARM tạo thư viện altium designer tạo xung vuông Tạp chí Tạp Chí Hay tăng áp Tập lệnh AT Team Support TEAMPLATE PROTEUS Test thị trường tài chính Thiết Bị Thú Vị Thiết kế robot Thiết lập Fuse Bits Thiết Lập Pin Thuật Toán Thuật Toán Điều Khiển PID Thuật Toán Quine MCCluskey Thư viện Protues Thực Hành Thyristor Timer Timer/Counter Tin Học Chia Sẻ Tổ Chức Bộ Nhớ tổng quan về proteus 8 Transistor Tranzito Tranzitor Trao đổi học tập Travel Trình Biên Dịch Trình Dịch Trong Suốt Truyền Thông Nối Tiếp Không Đồng Bộ- UART truyền thông nối tiếp RS232 Tụ điện TUT - 8051 - ASM TUT - 8051 - KeilC tự hành Tự Học C Tự Học Lập Trình C Tý hon UART Update USB Ứng Dụng Led Quảng Cáo ứng dụng mạch khuếch đại thuật toán vẽ mạch in vẽ mạch nguyên lý VHDL Vi Điều Khiển Vi điều khiển - Ứng dụng vi điều khiển PIC Vi mạch số Vi Xử Lý Vi Xử Lý 8051 Vi Xử Lý 8086 Vi Xử Lý ARM Vi Xử Lý PIC Video Video Mach Điện Virtual Serial Port Driver VOM vxl Web Design xác định góc quay động cơ xử lý chuỗi

I. Giới thiệu. 
      Bài này tiếp tục bài đầu tiên trong loạt bài giới thiệu về AVR, nếu sau bài "Làm quen AVR" bạn đã phần nào biết cách lập trình cho AVR bằng AVRStudio thì trong bài này, chúng ta sẽ tìm hiểu kỹ hơn về cấu trúc của AVR. Sau bài này, bạn sẽ:
  • Hiểu được cấu trúc AVR, cấu trúc bộ nhớ và cách thức hoạt động của chip.
  • Hiểu về Stack và cách hoạt động.
  • Biết được một số instruction cơ bản truy xuất bộ nhớ.
  • Học các instruction rẽ nhánh và vòng lặp.
  • Chương trình con (Subroutine) và Macro.
  • Cải tiến ví dụ trong bài 1.
  • Viết 1 ví dụ minh họa cách sử dụng bộ nhớ và vòng lặp.

II. Tổ chức của AVR.  
     AVR có cấu trúc Harvard, trong đó đường truyền cho bộ nhớ dữ liệu (data memory bus) và đường truyền cho bộ nhớ chương trình (program memory bus) được tách riêng. Data memory bus chỉ có 8 bit và được kết nối với hầu hết các thiết bị ngoại vi, với register file. Trong khi đó program memory bus có độ rộng 16 bits và chỉ phục vụ cho instruction registers. Hình 1 mô tả cấu trúc bộ nhớ của AVR.
     Bộ nhớ chương trình (Program memory): Là bộ nhớ Flash lập trình được, trong các chip AVR cũ (như AT90S1200 hay AT90S2313…) bộ nhớ chương trình chỉ gồm 1 phần là Application Flash Section nhưng trong các chip AVR mới chúng ta có thêm phần Boot Flash setion. Boot section sẽ được khảo sát trong các phần sau, trong bài này khi nói về bộ nhớ chương trình, chúng ta tự hiểu là Application section. Thực chất, application section bao gồm 2 phần: phần chứa các instruction (mã lệnh cho hoạt động của chip) và phần chứa các vector ngắt (interrupt vectors). Các vector ngắt nằm ở phần đầu của application section (từ địa chỉ 0x0000) và dài đến bao nhiêu tùy thuộc vào loại chip. Phần chứa instruction nằm liền sau đó, chương trình viết cho chip phải được load vào phần này. Xem lại phần đầu của ví dụ trong bài 1:
.ORG 0x000
RJMP BATDAU
.ORG 0x020
       Trong ví dụ này, ngay sau khi set vị trí 0x000 bằng chỉ thị (DIRECTIVE) .ORG 0x000 chúng ta dùng instruction RJMP để nhảy đến vị trí 0x020, như thế phần bộ nhớ chương trình từ 0x00 đến 0x01F không được sử dụng (vì trong  ví dụ này chúng ta không sử dụng các vector ngắt). Chương trình chính được bắt đầu từ địa chỉ 0x020, con số 0x020 là do người lập trình chọn, thật ra các vector ngắt của chip ATMEGA8 chỉ kéo dài đến địa chỉ 0x012, vì vậy chương trình chính có thể được bắt đầu từ bất cứ vị trí nào sau đó. Để biết độ dài các vector ngắt của từng chip bạn hãy tham khảo datasheet của chip đó.
      Vì chức năng chính của bộ nhớ chương trình là chứa instruction, chúng ta không có nhiều cơ hội tác động lên bộ nhớ này khi lập trình cho chip, vì thế đối với người lập trình AVR, bộ nhớ này “không quá quan trọng”. Tất cả các thanh ghi quan trọng cần khảo sát nằm trong bộ nhớ dữ liệu của chip.

Hình 1. Tổ chức bộ nhớ của AVR.
      Bộ nhớ dữ liệu (data memory): Đây là phần chứa các thanh ghi quan trọng nhất của chip, việc lập trình cho chip phần lớn là truy cập bộ nhớ này. Bộ nhớ dữ liệu trên các chip AVR có độ lớn khác nhau tùy theo mỗi chip, tuy nhiên về cơ bản phần bộ nhớ này được chia thành 5 phần:
      Phần 1: là phần đầu tiên trong bộ nhớ dữ liệu, như mô tả trong hình 1, phần này bao gồm 32 thanh ghi có tên gọi là register file (RF), hay General Purpose Rgegister – GPR, hoặc đơn giản là các Thanh ghi. Tất cả các thanh ghi này đều là các thanh ghi 8 bits như trong hình 2.

Hình 2. Thanh ghi 8 bits.
    Tất cả các chip trong họ AVR đều bao gồm 32 thanh ghi Register File có địa chỉ tuyệt đối từ 0x0000 đến 0x001F. Mỗi thanh ghi có thể chứa giá trị dương từ 0 đến 255 hoặc các giá trị có dấu từ -128 đến 127 hoặc mã ASCII của một ký tự nào đó…Các thanh ghi này được đặt tên theo thứ tự là R0 đến R31. Chúng được chia thành 2 phần, phần 1 bao gồm các thanh ghi từ R0 đến R15 và phần 2 là các thanh ghi R16 đến R31. Các thanh ghi này có các đặc điểm sau:
  • Được truy cập trực tiếp trong các instruction.
  • Các toán tử, phép toán thực hiện trên các thanh ghi này chỉ cần 1 chu kỳ xung clock.
  • Register File được kết nối trực tiếp với bộ xử lí trung tâm – CPU của chip.
  • Chúng là nguồn chứa các số hạng trong các phép toán và cũng là đích chứa kết quả trả lại của phép toán.
Để minh họa, hãy xét ví dụ thực hiện phép cộng 2 thanh ghi bằng instruction ADD như sau:
ADD R1, R2
       Bạn thấy trong dòng lệnh trên, 2 thanh ghi R1 và R2 được sử dụng trực tiếp với tên của chúng, dòng lệnh trên khi được dịch sang opcode để download vào chip sẽ có dạng: 0000110000010010 trong đó 00001=1 tức thanh ghi R1 và 00010 = 2 chỉ thanh ghi R2. Sau phép cộng, kết quả sẽ được lưu vào thanh ghi R1.
      Tất cả các instruction sử dụng RF làm toán hạng đều có thể truy nhập tất cả các RF một cách trực tiếp trong 1 chu kỳ xung clock, ngoại trừ SBCI, SUBI, CPI, ANDI và LDI, các instruction này chỉ có thể truy nhập các thanh ghi từ R16 đến R31.
      Thanh ghi R0 là thanh ghi duy nhất được sử dụng trong instruction LPM (Load Program Memory). Các thanh ghi R26, R27, R28, R29, R30 và R31 ngoài chức năng thông thường còn được sử dụng như các con trỏ (Pointer register) trong một số instruction truy xuất gián tiếp. Chúng ta sẽ khảo sát vấn đề con trỏ sau này. Hình 3 mô tả các chức năng phụ của các thanh ghi.

Hình 3. Register file.
      Tóm lại 32 RF của AVR được xem là 1 phần của CPU, vì thế chúng được CPU sử dụng trực tiếp và nhanh chóng, để gọi các thanh ghi này, chúng ta không cần gọi địa chỉ mà chỉ cần gọi trực tiếp tên của chúng. RF thường được sử dụng như các toán hạng (operand) của các phép toán trong lúc lập trình.
      Phần 2: là phần nằm ngay sau register file, phần này bao gồm 64 thanh ghi được gọi là 64 thanh ghi nhập/xuất (64 I/O register) hay còn gọi là vùng nhớ I/O (I/O Memory). Vùng nhớ I/O là cửa ngõ giao tiếp giữa CPU và thiết bị ngoại vi. Tất cả các thanh ghi điều khiển, trạng thái…của thiết bị ngoại vi đều nằm ở đây. Xem lại ví dụ trong bài 1, trong đó  phamhoangvuong.com có đề cập về việc điều khiển các PORT của AVR, mỗi PORT liên quan đến 3 thanh ghi DDRx, PORTx và PINx, tất cả 3 thanh ghi này đều nằm trong vùng nhớ I/O. Xa hơn, nếu muốn truy xuất các thiết bị ngoại vi khác như Timer, chuyển đổi Analog/Digital, giao tiếp USART…đều thực hiện thông qua việc điều khiển các thanh ghi trong vùng nhớ này.
     Vùng nhớ I/O có thể được truy cập như SRAM hay như các thanh ghi I/O. Nếu sử dụng instruction truy xuất SRAM để truy xuất vùng nhớ này thì địa chỉ của chúng được tính từ 0x0020 đến 0x005F. Nhưng nếu truy xuất như các thanh ghi I/O thì địa chỉ của chúng đựơc tính từ 0x0000 đến 0x003F.
     Xét ví dụ instruction OUT dùng xuất giá trị ra các thanh ghi I/O, lệnh này sử dụng địa chỉ kiểu thanh ghi, cấu trúc của lệnh như sau: OUT A, Rr, trong đó A là địa chỉ của thanh ghi trong vùng nhớ I/O, Rr là thanh ghi RF, lệnh OUT xuất giá trị từ thanh ghi Rr ra thanh ghi I/O có địa chỉ là A. Giả sử chúng ta muốn xuất giá trị chứa trong R6 ra thanh ghi điều khiển hướng của PORTD, tức thanh ghi DDRD, địa chỉ tính theo vùng I/O của thanh ghi DDRD là 0x0011, như thế câu lệnh của chúng ta sẽ có dạng: OUT 0x0011, R6. Tuy nhiên trong 1 trường hợp khác, nếu muốn truy xuất DDRD theo dạng SRAM, ví dụ lệnh STS hay LDS, thì phải dùng địa chỉ tuyệt đối của thanh ghi này, tức giá trị 0x0031, khi đó lệnh OUT ở trên được viết lại là STS 0x0031, R6.
     Để thống nhất cách sử dụng từ ngữ, từ bây giờ chúng ta dùng khái niệm “địa chỉ I/O” cho các thanh ghi trong vùng nhớ I/O để nói đến địa chỉ không tính phần Register File, khái niệm “địa chỉ bộ nhớ” của thanh ghi là chỉ địa chỉ tuyệt đối của chúng trong SRAM. Ví dụ thanh ghi DDRD có “địa chỉ I/O” là 0x0011 và “địa chỉ bộ nhớ” của nó là 0x0031, “địa chỉ bộ nhớ” = “địa chỉ I/O” + 0x0020.
     Vì các thanh ghi trong vùng I/O không được hiểu theo tên gọi như các Register file, khi lập trình cho các thanh ghi này, người lập trình cần nhớ địa chỉ của từng thanh ghi, đây là việc tương đối khó khăn. Tuy nhiên, trong hầu hết các phần mềm lập trình cho AVR, địa chỉ của tất cả các thanh ghi trong vùng I/O đều được định nghĩa trước trong 1 file Definition, bạn chỉ cần đính kèm file này vào chương trình của bạn là có thể truy xuất các thanh ghi với tên gọi của chúng. Giả sử trong ví dụ ở bài 1, để lập trình cho chip Atmega8 bằng AVRStudio, dòng thứ 2 chúng ta sử dụng INCLUDE "M8DEF.INC" để load file định nghĩa cho chip ATMega8, file M8DEF.INC. Vì vậy, trong sau này khi muốn sử dụng thanh ghi DDRD bạn chỉ cần gọi tên của chúng, như: OUT DDRD,R6.
     Phần 3: RAM tĩnh, nội (internal SRAM), là vùng không gian cho chứa các biến (tạm thời hoặc toàn cục) trong lúc thực thi chương trình, vùng này tương tự các thanh RAM trong máy tính nhưng có dung lượng khá nhỏ (khoảng vài KB, tùy thuộc vào loại chip).
     Phần 4: RAM ngoại (external SRAM), các chip AVR cho phép người sử dụng gắn thêm các bộ nhớ ngoài để chứa biến, vùng này thực chất chỉ tồn tại khi nào người sử dụng gắn thêm bộ nhớ ngoài vào chip.
     Phần 5: EEPROM (Electrically Ereasable Programmable ROM) là một phần quan trọng của các chip AVR mới, vì là ROM nên bộ nhớ này không bị xóa ngay cả khi không cung cấp nguồn nuôi cho chip, rất thích hợp cho các ứng dụng lưu trữ dữ liệu. Như trong hình 1, phần bộ nhớ EEPROM được tách riêng và có địa chỉ tính từ 0x0000.
     Câu hỏi bây giờ là AVR hoạt động như thế nào?
     Hình 4 biểu diễn cấu trong bên trong của 1 AVR. Bạn thấy rằng 32 thanh ghi trong Register File được kết nối trực tiếp với Arithmetic Logic Unit -ALU (ALU cũng được xem là CPU của AVR) bằng 2 line, vì thế ALU có thể truy xuất trực tiếp cùng lúc 2 thanh ghi RF chỉ trong 1 chu kỳ xung clock (vùng được khoanh tròn màu đỏ trong hình 4).

Hình 4. Cấu trúc bên trong AVR.
      Các instruction được chứa trong bộ nhớ chương trình Flash memory dưới dạng các thanh ghi 16 bit. Bộ nhớ chương trình được truy cập trong mỗi chu kỳ xung clock và  1 instruction chứa trong program memory sẽ được load vào trong instruction register, instruction register tác động và lựa chọn register file cũng như RAM cho ALU thực thi. Trong lúc thực thi chương trình, địa chỉ của dòng lệnh đang thực thi được quyết định bởi một bộ đếm chương trình – PC (Program counter). Đó chính là cách thức hoạt động của AVR.
      AVR có ưu điểm là hầu hết các instruction đều được thực thi trong 1 chu kỳ xung clock, vì vậy có thể nguồn clock lớn nhất cho AVR có thể nhỏ hơn 1 số vi điều khiển khác như PIC nhưng thời gian thực thi vẫn nhanh hơn.
III. Stack.
      Stack được hiểu như là 1 “tháp” dữ liệu, dữ liệu được chứa vào stack ở đỉnh “tháp” và dữ liệu cũng được lấy ra từ đỉnh. Kiểu truy cập dữ liệu của stack gọi là LIFO (Last In First Out – vào sau ra trước). Hình 5 thể hiện cách truy cập dữ liệu của stack.

Hình 5. Stack.
        Khái niệm và cách thức hoạt động của stack có thể được áp dụng cho AVR, bằng cách khai báo một vùng nhớ trong SRAM là stack ta có thể sử dụng vùng nhớ này như một stack thực thụ.
       Để khai báo một vùng SRAM làm stack chúng ta cần xác lập địa chỉ đầu của stack bằng cách xác lập con trỏ stack-SP (Stack Pointer). SP là 1 con trỏ 16 bit bao gồm 2 thanh ghi 8 bit SPL và SPH (chữ L là LOW chỉ thanh ghi mang giá trị byte thấp của SP, và H = HIGH), SPL và SPH nằm trong vùng nhớ I/O. Giá trị gán cho thanh ghi SP sẽ là địa chỉ khởi động của stack. Quay lại ví dụ ở bài 1, phần khởi tạo các điều kiện đầu.
; KHOI TAO CÁC DIEU KIEN DAU
LDI R16, HIGH(RAMEND)
LDI R17, LOW(RAMEND)
OUT SPH, R16
OUT SPL, R17
       Bốn dòng khai báo trên mục đích là gán giá trị của RAMEND cho con trỏ SP, RAMEND (tức End of Ram) là biến chứa địa chỉ lớn nhất của RAM nội trong AVR, biến này được định nghĩa trong file M8DEF.INC. Như thế sau 4 dòng trên, con trỏ SP chứa giá trị cuối cùng của SRAM hay nói cách khác vùng stack bắt đầu từ vị trí cuối cùng của bộ nhớ SRAM. Nhưng tại sao là vị trí cuối cùng mà không là 1 giá trị khác. Có thể giải thích như sau: stack trong AVR hoạt động từ trên xuống, sau khi dữ liệu được đẩy vào stack, SP sẽ giảm giá trị vì thế khởi động SP ở vị trí cuối cùng của SRAM sẽ tránh được việc mất dữ liệu do ghi đè. Bạn có thể khởi động stack với 1 địa chỉ khác, tuy nhiên vì lý do an toàn, nên khởi động stack ở RAMEND.
      Hai instruction dùng cho truy cập stack là PUSH và POP, trong đó PUSH dùng đẩy dữ liệu vào stack và POP dùng lấy dữ liệu ra khỏi stack. Dữ liệu được đẩy vào và lấy ra khỏi stack tại vị trí mà con trỏ SP trỏ đến. Ví dụ cho chip ATMega8, RAMEND=0x045F, sau khi khởi động, con trỏ SP trỏ đến vị trí 0x045F trong SRAM, nếu ta viết các câu lệnh sau:

LDI R16, 1
PUSH R16
LDI R16, 5PUSH R16LDI R16, 8PUSH R16
       Khi đó nội dung của stack sẽ như trong hình 6.

Hình 6. Nội dung stack trong ví dụ.
       Sau mỗi lần PUSH dữ liệu, SP sẽ giảm 1 đơn vị và trỏ vào vị trí tiếp theo.
Bây giờ nếu ta dùng POP để lấy dữ liệu từ stack, POP R2, thì R2 sẽ mang giá trị của ngăn nhớ 0x045D, tức R2=8. Trước khi instruction POP được thực hiện, con trỏ SP được tăng lên 1 đơn vị, sau đó dữ liệu sẽ được lấy ra từ vị trí mà SP trỏ đến trong stack.
       Stack trong AVR không phải là “vô đáy”, nghĩa là chúng ta chỉ có thể PUSH dữ liệu vào stack ở 1 độ sâu nhất định nào đấy (phụ thuộc vào chip). Sử dụng stack không đúng cách đôi khi sẽ làm chương trình thực thi sai hoặc tốn thời gian thực thi vô ích. Vì thế không nên sử dụng stack chỉ để lưu các biến thông thường. Ứng dụng phổ biến nhất của stack là sử dụng trong các chương trình con (Subroutine), khi chúng ta cần “nhảy” từ một vị trí trong chương trình chính đến 1 chương trình con, sau khi thực hiện chương trình con lại muốn quay về vị trí ban đầu trong chương trình chính thì Stack là phương cách tối ưu dùng để chứa bộ đếm chương trình trong trường hợp này. Xem lại ví dụ trong bài 1, trong chương trình chính chúng ta dùng lệnh RCALL DELAY để nhảy đến đoạn chương trình con DELAY, RCALL là lệnh nhảy đến 1 vị trí trong bộ nhớ chương trình, trước khi nhảy, PC được cộng thêm 1 và PUSH một cách tự động vào stack. Cuối chương trình con DELAY, chúng ta dùng instruction RET, instruction này POP dữ liệu từ stack ra PC một cách tự động, bằng cách này chúng ta có thể quay lại vị trí trước đó. Chính vì các lệnh RCALL và RET sử dụng stack một cách tự động nên ta phải khởi động stack ngay từ đầu, nếu không chương trình sẽ thực thi sai chức năng.
      Tóm lại cần khởi động stack ở đầu chương trình và không nên sử dụng stack một cách tùy thích nếu chưa thật cần thiết.
IV. Thanh ghi trạng thái - SREG (STATUS REGISTRY).
       Nằm trong vùng nhớ I/O, thanh ghi SREG có địa chỉ I/O là 0x003F và địa chỉ bộ nhớ là 0x005F (thường đây là vị trí cuối cùng của vùng nhớ I/O) là một trong số các thanh ghi quan trọng nhất của AVR, vì thế mà  phamhoangvuong.com dành phần này để giới thiệu về thanh ghi này. Thanh ghi SREG chứa 8 bit cờ (flag) chỉ trạng thái của bộ xử lí, tất cả các bit này đều bị xóa sau khi reset, các bit này cũng có thể được đọc và ghi bởi chương trình. Chức năng của từng bit được mô tả như sau:

Hình 7. Thanh ghi trạng thái.
  • Bit 0 – C (Carry Flag: Cờ nhớ): là bit nhớ trong các phép đại số hoặc logic, ví dụ thanh ghi R1 chứa giá trị 200, R2 chứa 70, chúng ta thực hiện phép cộng có nhớ: ADC R1, R2, sau phép cộng, kết quả sẽ được lưu lại trong thanh ghi R1, trong khi kết quả thực là 270 mà thanh ghi R1 lại chỉ có khả năng chứa tối đa giá trị 255 (vì có 8 bit) nên trong trường hợp này, giá trị lưu lại trong R1 thực chất chỉ là 14, đồng thời cờ C được set lên 1 (vì 270=100001110, trong đó 8 bit sau 00001110 =14 sẽ được lưu lại trong R1).
  • Bit 1 – Z (Zero Flag: Cờ 0): cờ này được set nếu kết quả phép toán đại số hay phép Logic bằng 0.
  • Bit 2 – N (Negative Flag: Cờ âm): cờ này được set nếu kết quả phép toán đại số hay phép Logic là số âm.
  • Bit 3 – V (Two’s complement Overflow Flag: Cờ tràn của bù 2): hoạt động của cờ này có vẻ sẽ khó hiểu cho bạn vì nó liên quan đến kiến thức số nhị phân (phần bù), chúng ta sẽ đề cập đến khi nào thấy cần thiết.
  • Bit 4  – S (Sign Bit: Bit dấu): Bit S là kết quả phép XOR giữa 1 cờ N và V, S=N xor V.
  • Bit 5 – H (Half Carry Flag: Cờ nhờ nữa): cờ H là cờ nhớ trong 1 vài phép toán đại số và phép Logic, cờ này hiệu quả đối với các phép toán với số BCD.
  • Bit 6 – T (Bit Copy Storage): được sử dụng trong 2 Instruction BLD (Bit LoaD) và BST (Bit STorage). phamhoangvuong.com  sẽ giải thích chức năng Bit T trong phần giới thiệu về BLD và BST.
  • Bit 7 – I (Global Interrupt Enable) : Cho phép ngắt toàn bộ): Bit này phải được set lên 1 nếu trong chương trình có sử dụng ngắt. Sau khi set bit này, bạn muốn kích hoạt loại ngắt nào cần set các bit ngắt riêng của ngắt đó. Hai instruction dùng riêng để Set và Clear bit I là SEI và CLI.
      Chú ý: tất cả các bit trong thanh ghi SREG đều có thể được xóa thông qua các instruction không toán hạng CLx và set bởi SEx, trong đó x là tên của Bit.Ví dụ CLT là xóa Bit T và SEI là set bit I.
phamhoangvuong.com  chỉ giải thích ngắn gọn chức năng của các bit trong thanh ghi SREG, cụ thể chức năng và cách sử dụng của từng bit chúng ta sẽ tìm hiểu trong các trường hợp cụ thể sau này, người đọc có thể tự tìm hiểu thêm trong các tài liệu về INSTRUCTION cho AVR.
      phamhoangvuong.com  cung cấp thêm 1 bảng tóm tắt sự ảnh hưởng của các phép toán đại số, logic lên các Bit trong thanh ghi SREG.

Hình 8. Ảnh hưởng của các phép toán lên SREG.
IV. Macro và chương trình con.
       Macro là khái niệm chỉ một đoạn code nhỏ để thực hiện một công việc nào đó, nếu có 1 đoạn code nào đó mà bạn rất hay sử dụng khi lập trình thì bạn nên dùng macro để tránh việc phải viết đi viết lại đoạn code đó. Lập trình ASM cho AVR cho phép bạn sử dụng Macro, để tạo 1 Macro bạn sử dụng DIRECTIVE.
.MACRO delay4
NOP
NOP
NOP
NOP
.ENDMACRO
       Đoạn Macro trên có tên delay4 thực hiện việc delay 4 chu kỳ máy bằng 4 lệnh NOP, nếu trong chương trình bạn cần dùng Macro này thì chỉ cần gọi  delay4 ở bất kỳ dòng nào.
[…] ; code của bạn
Delay4
[…] ; code của bạn
       Mỗi lần tên của Macro được gọi, trình biên dịch sẽ tìm đến Macro đó và copy toàn bộ nội dung Macro vào vị trí bạn gọi. Như vậy thực chất con trỏ chương trình không nhảy đến Macro, Macro không làm giảm dung lương chưong trình mà chỉ làm cho việc lập trình nhẹ nhàng hơn.  Đây chính là khác biệt lớn nhất của Macro và Subroutine (chương trình con).
       Chương trình con cũng là 1 đoạn code thực hiện 1 chức năng đặc biệt nào đó. Tuy nhiên khác với Macro, mỗi khi gọi chương trình con, con trỏ chương trình nhảy đến chương trình con đề thực thi chương trình con và sau đó quay về chương trình chính. Như thế chương trình con chỉ được biên dịch 1 lần và có thể sử dụng nhiều lần, nó làm giảm dung lượng chưong trình. Đây là  ưu điểm và cũng là điểm khác biệt lớn nhất giữa chương trình con và Macro. Tuy nhiên cần chú ý là việc nhảy đến chương trình con và nhảy về chương trình chính cần vài chu kỳ máy, có thể làm chậm chương trình, đây là nhược điểm của chương trình con so với macro.
      Chương trình con cho AVR luôn được bắt đầu bằng 1 Label, đó cũng là tên và địa chỉ của chương trình con. Chương trình con thường được kết thúc với câu lệnh RET (Return). Chúng ta đã biết về chương trình con qua ví dụ của bài 1, trong đó DELAY là 1 chương trình con.
       Để gọi chương trình con từ 1 vị trí nào đó trong chương trình, chúng ta có thể dùng lệnh CALL hoặc RCALL(Relative CALL) (xem lại ví dụ bài 1 về cách sử dụng RCALL). Mỗi khi các lệnh này được gọi, bộ đếm chương trình được tự động được PUSH vào stack và khi chương trình con kết thúc  bằng lệnh RET, bộ đếm chương trình được POP trở ra và quay về chương trình chính. Lệnh CALL có thể gọi 1 chương trình con ở bất kỳ vị trí nào trong khi RCALL chỉ gọi trong khoảng bộ nhớ 4KB, nhưng RCALL cần ít chu kỳ xung clock hơn khi thực thi.
       Hai instruction khác có thể được dùng để gọi chương trình con đó là JMP (Jump) và RJMP (Relative Jump). Khác với các lệnh call, các lệnh jump không cho phép quay lại vì không tự động PUSH bộ đếm chương trình vào Stack, để sử dụng các lệnh này gọi chương trình con bạn cần một số lệnh jump khác ở cuối chương trình con.
       Tóm lại bạn nên viết 1 chương trình con đúng chuẩn và dùng CALL hoặc RCALL để gọi chương các chương trình này, chỉ những trường hợp đặc biệt hoặc bạn hiểu rất rõ về chúng thì có thể dùng các lệnh jump.
V. Ví dụ minh họa.
       Nếu bạn đã đọc và hiểu đến thời điểm này thì bạn đã có thể hiểu hết hoạt động của chương trình ví dụ trong bài 1, thật sự ví dụ đó rất đơn giản và dễ hiểu. Tuy nhiên, bạn có thề tối ưu hóa ví dụ đó theo hướng làm giảm dung lượng chương trình và tất nhiên, chương trình sẽ khó hiểu hơn cho người khác. Các phần khởi động vị trí bộ nhớ, stack và chương trình con DELAY chúng ta không thay đổi, chỉ thay đổi phần chương trình chính, 1 trong những cách viết chương trình chính như cách sau:
; CHUONG TRINH CHINH , BAI 1, VI DU 1, VERSION 2///////////////////////////////
LDI R16, $1 ;LOAD GIA TRI KHOI DONG CHO  R16
MAIN:
OUT PORTB, R16 ; XUAT GIA TRI TRONG R16 RA PORTB
RCALL DELAY ; GOI CHUONG TRINH CON DELAY
ROL R16 ; XOAY THANH GHI R16 SANG TRAI 1 VI TRI
RJMP MAIN ; NEU R16 ≠0, NHAY VE MAIN, TIEP TUC QUET
;/////////////////////////////////////////////////////////////////////////////////////////
       Có thể không cần giải thích bạn cũng đã có thể hiểu đoạn code trên, đây chỉ là 1 trong những cách có thể, bạn hãy viết lại theo cách của riêng bạn với yêu cầu là chương trình phải thực hiện đúng chức năng và ngắn gọn.
      Bây giờ chúng ta sẽ thực hiện một ví dụ minh họa cho những gì chúng ta đã học trong bài 2 này. Nội dung của ví dụ thể hiện trong mạch điện hình 9. Hoạt động của mạch điện tử như sau: 1 chip ATMega8 được sử dụng như một counter, có thể dùng để đếm lên và đếm xuống, 2 button trong mạch điện tác động như 2 “kicker”, nhấn button 1 để đếm lên và button để đếm xuống, giá trị đếm nằm trong khoảng từ 0 đến 9. Giá trị đếm được hiển thị trên 1 LED 7 đoạn loại anod chung (dương chung), chip 7447 được dùng để giải mã từ giá trị BCD xuất ra bởi ATMega8 sang tín hiệu cho LED 7 đoạn anod chung, chúng ta cần sử dụng 7447 vì tín hiệu xuất ra từ chip ATMega8 là dạng nhị phân hoặc BCD , tín hiệu này không thể hiển thị trực tiếp trên các LED 7 đoạn, chip 7447 có nhiệm vụ chuyển 1 dữ liệu dạng digit BCD sang mã phù hợp cho LED 7 đoạn.
       Để thực hiện ví dụ, trước hết bạn hãy vẽ mạch điện như trong hình 9 bằng phần mềm Proteus (xem cách vẽ mạch điện bằng Proteus), mạch điện chỉ có 5 loại linh kiện là chip ATMega8 (từ khóa mega8), 1 LED 7 đoạn anod chung với tên đầy đủ trong Proteus là 7SEG-COM-AN-GRN (từ khóa 7SEG), 1 chip 7447 (từ khóa 7447), 1 điện trở 10 Ω và 2 button (từ khóa button).

Hình 9. Ví dụ cho bài 2.
      Sử dụng AVRStudio tạo 1 project mới với tên gọi avr2 (xem lại cách tạo Project mới trong AVRStudio). Viết lại phần code bên dưới vào vào file avr2.asm
List 1. Ví dụ cấu trúc AVR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
.INCLUDE "M8DEF.INC"
.CSEG.
.ORG 0x0000
      RJMP BATDAU
.ORG 0x0020
BATDAU:
;KHOI DONG STACK POINTER
      LDI R17, HIGH(RAMEND)      LDI R16, LOW(RAMEND)      OUT SPL, R16      OUT SPH,R17; KHOI DONG CAC PORT      CLR R16 ; XOA R16, R16=0      OUT DDRB, R16 ; DDRB=0, PORTB LA NGO NHAP       LDI R16, 0xFF ; SET TAT CA CAC BIT CUA R16 LEN 1      OUT PORTB,R16 ;DDRB=0, PORTB =0xFF, KEO LEN CAC CHAN PORTB      OUT DDRD, R16 ;DDRD=0xFF, PORTD LA NGO XUAT      CLR R25 ;XOA R25, R25 LA THANH GHI DUNG CHUA SO DEM      SER R20 ; R20 LA THANH GHI TAM CHUA GIA TRI TRUOC DO CUA PINB
MAIN:
      IN R21,PINB ;DOC GIA TRI TU PINB, TUC TU CAC BUTTON      RCALL SOSANH  ;GOI CHUONG TRINH CON SOSANH      OUT PORTD, R25 ;XUAT GIA TRI DEM RA PORTD      SBRS R21,0 ;NEU BIT 0 CUA R21 (TUC CHAN PB0) =1 THI BO QUA DONG ;TIEP THEO      RCALL TANG ;NHAY DEN CHUONG TRINH CON TANG GIA TRI DEM      SBRS R21,1 ;NEU BIT 1 CUA R21 (TUC CHAN PB1) =1 THI BO QUA DONG ;TIEP THEO      RCALL GIAM ;NHAY DEN CHUONG TRINH CON GIAM GIA TRI DEM      MOV R20,R21 ;LUU LAI TRANG THAI PINB      RJMP MAIN
;**********************CHUONG TRINH CON************************
; **************subroutine kiem tra gioi hang (tu 0 den 9) cua so dem

SOSANH:
      CPI R25, 10      BREQ RESET0 ;NEU GIA TRI DEM=10 THI TRA VE 0      CPI R25, 255      BREQ RESET9 ;NEU GIA TRI DEM =255 THI TRA VE 9      RJMP QUAYVE ;NHAY DEN NHAN QUAYVE
RESET0:
      LDI R25,$0 ;TRA GIA TRI DEM VE 0      RJMP QUAYVE
RESET9:
      LDI R25,$9 ;GAN 9 CHO GIA TRI DEM
QUAYVE:
      RET
; ************************************************************
; **************subroutine tang so dem 1 don vi neu dieu kien thoa
TANG:
      SBRS R20,0      RET      INC R25      RET
; **************subroutine giam so dem 1 don vi neu dieu kien thoa
GIAM:
      SBRS R20,1      RET
      DEC R25      RET
      Trong ví này này, chúng ta sử dụng 2 PORT của chip ATMega8, PORTD dùng xuất dữ liệu (số đếm) ra chip 7447 và sau đó hiển thị trên LED 7 đoạn. PORTB dùng như ngõ nhập, tín hiệu từ các button sẽ được chip ATMega8 nhận thông qua 2 chân PB0 và PB1 của PORTB.
      Hoạt động của cac PORT và việc xác lập 1 PORT như các ngõ xuất chúng ta đã khảo sát trong bài 1. Ở đây chúng ta khảo sát thêm về xác lập PORT như 1 ngõ nhập, trước hết bạn hãy quan sát mạch điện tương đương của 1 chân trong các PORT xuất nhập của AVR trong hình 10.

Hình 10. Cấu trúc chân trong PORT của AVR.
      Trong mạch điện hình 10, các diode và tụ điện chỉ có chức năng bảo vệ chân PORT, nhưng điện trở Rpu (R Pull up) đóng vai trò quan trọng như là điện trở kéo lên khi chân của PORT làm nhiệm vụ nhận tín hiệu (ngõ nhập). Tuy nhiên trong AVR, điện trở kéo lên này không phải luôn kích hoạt, chúng ta biết rằng mỗi PORT của AVR có 3 thanh ghi: DDRx, PORTx và PINx, nếu DDRx=0 thì PORT x là ngõ nhập, lúc này thanh ghi PINx là thanh ghi chứa dữ liệu nhận về, đặc biệt thanh ghi PORTx vẫn được sử dụng trong mode này, đó là thanh ghi xác lập điện trở kéo lên, như thế nếu DDRx=0 và PORTx=0xFF thì các chân PORTx là ngõ nhập và được kéo lên bởi 1 điện trở trong chip, nghĩa là các chân của PORTx luôn ở mức cao, muốn kích để thay đồi trạng thái chân này chúng ta cần nối chân đó trực tiếp với GND, đấy là lý do tại sao các button trong mạch điện của chúng ta có 1 đầu nối với chân của chip còn đầu kia được nối với GND. Đây cũng là ý nghĩa của khái niệm điện trở kéo lên (Pull up resistor) trong kỹ thuật điện tử. Đoạn code trong phần “KHOI DONG CAC PORT” của ví dụ này xác lập PORTD là ngõ xuất (DDRD=0xFF) , PORTB là ngõ nhập có sử dụng điện trở kéo lên (DDRB=0, PORTB=0xFF).
      Chúng ta sẽ giải thích hoạt động của đoạn chương trình chính và các đoạn chương trình con. Trước hết, trong chương trình này, chúng ta sử dụng 3 thanh ghi chính là R20, R21 và R25, trong đó R25 là thanh ghi chứa số đếm, giá trị của thanh ghi R25 sẽ được xuất ra PORTD của chip, thanh ghi R21 chứa trạng thái của thanh ghi PINB và cũng là trạng thái của các button, thanh ghi R20 kết hợp với thanh ghi R21 tạo thành 1 “bộ đếm cạnh xuống” của các button. Để hiểu thấu đáo hoạt động đếm (cũng là hoạt động chính của ví dụ này) chúng ta xét trạng thái chân PB0 như trong hình 11.

Hình 11. Thay đổi trạng thái ở các chân I/O.
      Trong trạng thái bình thường (button không được nhấn), chân PB0 ở mức cao (do điện trở kéo lên), bộ đếm không hoạt động, giá trị đếm không thay đổi, bây giờ nếu nhấn button, chân PB0 được nối trực tiếp với GND, chân này sẽ bị kéo xuống mức thấp, bằng cách kiểm tra trạng thái chân PB0, nếu PB0=0 ta tăng giá trị đếm 1 đơn vị. Ý tưởng như thế có vẻ hợp lý, tuy nhiên nếu áp dụng thì chương trình sẽ hoạt động không đúng chức năng, khi bạn nhấn 1 lần giá, trị đếm có thể tăng đến cả trăm hoặc không kiểm soát được, hiệu ứng này tương tự khi bạn nhấn và giữ 1 phím trên bàn phím máy tính, lý do là vì chúng ta sử dụng phương pháp kiểm tra mức để đếm, thời gian quét của chương trình  rất ngắn so với thời gian chúng ta giữ button. Để khắc phục, chúng ta dùng phương pháp kiểm tra cạnh xuống, chỉ khi nào phát hiện chân PB0 thay đổi từ 1 xuống 0 thì mới tăng giá trị đếm 1 đơn vị, kết quả là mỗi lần nhấn button thì giá trị đếm chỉ tăng 1 (ngay cả khi ta nhấn và giữ button), thanh ghi R20 được sử dụng để lưu trạng thái trước đó của PINB (cũng là trạng thái của các button).
      Trong chương trình, THIETKEMACHDIENTU.NET sử dụng 2 istruction mới là SBRC và SBRS để kiểm tra trạng thái các chân của PORTB (button). SBRC – Skip if Bit in Register is Clear, lệnh này sẽ bỏ qua 1 dòng lệnh ngay sau đó (chỉ bỏ qua 1 dòng duy nhất) nếu 1 bit trong thanh ghi ở mức 0, SBRC – Skip if Bit in Register is Set- hoạt động tương tự SBRC nhưng skip sẽ xảy ra nếu bit trong thanh ghi ở mức 1. Dựa vào đây chúng ta giải thích 4 dòng sau:
SBRS R21,0 ;NEU BIT 0 CUA R21 (TUC CHAN PB0) =1 THI BO QUA DONG ;TIEP THEO
RCALL TANG ;NHAY DEN CHUONG TRINH CON TANG GIA TRI DEM
SBRS R21,1 ;NEU BIT 1 CUA R21 (TUC CHAN PB1) =1 THI BO QUA DONG ;TIEP THEO
RCALL GIAM ;NHAY DEN CHUONG TRINH CON GIAM GIA TRI DEM
       Dòng 1 dùng kiểm tra trạng thái bit 0 trong R21 (chú ý R21 chứa giá trị của PINB), nếu bit này bằng 1 (set), tức chân PB0=1 hay button không được nhấn, thì nhảy bỏ qua dòng lệnh tiếp theo để đến dòng 3. Ở dòng 3 chương trình kiểm tra trạng thái chân PB1 (button thứ 2). Quay lại dòng 1, nếu chương trình kiểm tra phát hiện chân PB0=0 (button thứ nhất được nhấn) thì dòng lệnh thứ 2 được thực thi, kết quả là chương trình nhảy đến chương trình con TANG.
TANG:
SBRS R20,0
RET
INC R25
RET
       Dòng đầu tiên của chương trình con TANG là kiểm tra trạng thái trước đó của chân PB0 (được lưu ở bit 0 trong thanh ghi R20), nếu trạng thái này bằng 0, nghĩa là không có sự chuyển từ 1 xuống 0 ở chân PB0, dòng 2 (lệnh RET) sẽ được thực thi để quay về chương trình chính. Nhưng nếu PB0 trước đó bằng 1, nghĩa là có sự thay đổi từ 1->0 ở chân này, giá trị đếm sẽ được tăng thêm 1 nhờ INC R25, sau đó quay về chương trình chính.
Tóm lại muốn tăng giá trị đếm thêm 1 đơn vị cần thỏa mãn 2 điều kiện: chân PB0 hiện tại =0 (button đang được nhấn) và trạng thái trước đó của PB0 phải là 1 (tránh trường hợp tăng liên tục). Phương pháp  này có thể áp dụng cho rất nhiều trường hợp đếm dạng đếm xung.
      Quá trình giảm giá trị đếm được hiểu tương tự, phần còn lại của ví dụ này bạn đọc hãy tự giải thích theo những gợi ý trên.
Nguồn :hocavr.com
ĐIỆN TỬ CHIA SẺ
Sáng Lập Bởi:
phamhoangvuong.com 

NGUỒN: HOCAVR.COM

Code AVR - C, học AVR, Làm quen AVR, Lập Trình AVR, Mã AVR - C, tài liệu avr, Tài Liệu Vi Điều Khiển,

Đăng nhận xét

Author Name

{picture https://lh3.googleusercontent.com/-5Ns_H0UG4cU/Vuu5KrqbEnI/AAAAAAAAEZs/XgsHemy00D4eQ3ivA76v6FFEm7jg9reVwCCo/s512-Ic42/pham-van-ngoc-anh.jpg}

Tôi là Ngọc Anh. Tôi đến từ Nghệ An. Tôi tốt nghiệp một trường đại học tại Sài Gòn. Hiện tôi đang phát triển công ty riêng. Liên lạc với tôi qua:

{facebook https://www.facebook.com/phamvanngocanh}
{twitter https://twitter.com/nghiphong1993}
{google https://plus.google.com/+dientuchiase/posts}
{youtube https://www.youtube.com/channel/UCeJKhA_goBNFmDw6RKNtmYQ}

Biểu mẫu liên hệ

Tên

Email *

Thông báo *

Được tạo bởi Blogger.