Giới thiệu chung
Các cổng vào ra của AVR đều có tính năng true Read – Modify – Write khi sử dụng chúng như các cổng vào ra thường. Nghĩa là chiều vào/ra của một chân có thể thay đổi riêng biệt mà không làm ảnh hưởng tới chiều vào/ra của các chân khác bằng các lệnh SBI và CBI. Tương tự với giá trị out ra (khi cấu hình cổng là cổng output) cũng như là việc cho phép hay không cho phép trở treo ở các chân. Bộ đệm ra có đặc tính lái dòng đối xứng với cả việc hút và tạo dòng. Bộ lái dòng đủ mạnh để điều khiển LED (20 mA theo như [1]). Các chân có thể chọn điện trở treo một cách độc lập. Tất cả các chân đều có diode bảo vệ. Dễ dàng thấy từ hình vẽ dưới các diode này sẽ bảo vệ các chân khỏi việc điện áp quá lớn từ ngoài ảnh hưởng đến vi điều khiển. Khi điện áp lớn hơn Vcc hoặc nhỏ hơn GND nó sẽ bị hạn biên về giá trị Vcc hoặc GND.
Mỗi PORT chiếm 3 địa chỉ trong vùng bộ nhớ vào/ra cho thanh ghi dữ liệu PORT, thanh ghi chiều vào/ra của PORT và PORT INPUT PINS. Riêng PORT INPUT PINS là bộ nhớ chỉ đọc. Thêm vào đó, bit Pull – Up Disable, PUD chứa trong thanh ghi SFIOR sẽ không cho phép điện trở pull up khi set bit này.
Sơ đồ khối của cổng
Cấu hình cho PORT
Mỗi PORT có 3 thanh ghi chứa các bit PORTxn, DDxn và PINxn (x=A..D, n= 0..7) nằm trong các thanh ghi tương ứng PORTx, DDRx và PINx.
Các bit DDxn trong thanh ghi DDRx cho phép chọn chiều của một chân trong PORT. Khi bit này set thì chân đó sẽ được cấu hình là chân output và khi bit này clear thì chân đó sẽ được cấu hình là chân input.
Khi bit PORTxn được set và chân được cấu hình là input thì điện trở treo sẽ được kích hoạt. Để tắt điện trở treo nội, bit PORTxn phải được xoá hoặc chân được cấu hình là chân output.
Khi chuyển giữa trạng thái tri-state ({DDxn,PORTxn}=0b00) và output high ({DDxn,PORTxn}=0b11) cần phải qua một trạng thái trung gian là cho phép pull-up ({DDxn,PORTxn}=0b10) hoặc output low ({DDxn,PORTxn}=0b01). Khuyến nghị là nên chọn trạng thái trung gian là cho phép pull up.
Tương tự khi chuyển giữa trạng thái pull up và output low.
Đọc giá trị tức thời tại các chân VĐK
Mặc dù cùng là VĐK của Atmel nhưng khác với họ AT89xx, dòng AVR qui định rõ ràng và tách bạch nhiệm vụ đọc ghi của từng chân trong cổng thông qua thanh ghi DDRx. Tuy nhiên, AVR cũng rất mềm dẻo trong việc đọc giá trị tức thời của các chân mà không cần phải biết chân đó đang dùng là chân vào hay ra nhờ vào thanh ghi PINxn. Giá trị trên thanh ghi PINxn chính là giá trị tức thời của chân n (n=0..7) ở port x (x=A..D).
Ở đầu vào thanh ghi PINx có sử dụng các bộ chốt D-trigger giúp ổn định giá trị đầu vào. Tuy nhiên nó cũng gây ra sự trễ nho nhỏ khi đọc giá trị. Do vậy, khi đọc giá trị gửi ra trong phần mềm thì cần có một lệnh trễ giữa lệnh gửi ra và lệnh đọc vào.
Một số chú ý:
- Riêng PortA được cấp nguồn từ AVCC. Vì vậy khi sử dụng PortA cần nối AVCC với +VCC. Trong trường hợp dùng chức năng ADC của PortA thì AVCC nên nối với +VCC thông qua mạch lọc.
Một vài ví dụ sử dụng cổng vào ra
Một ví dụ cơ bản đó là quét bàn phím:
[1] ATMEGA8535 datasheet
Rất mong nhận được sự đóng góp, thảo luận của mọi người để tôi có thể hoàn thiện vấn đề này.
Các cổng vào ra của AVR đều có tính năng true Read – Modify – Write khi sử dụng chúng như các cổng vào ra thường. Nghĩa là chiều vào/ra của một chân có thể thay đổi riêng biệt mà không làm ảnh hưởng tới chiều vào/ra của các chân khác bằng các lệnh SBI và CBI. Tương tự với giá trị out ra (khi cấu hình cổng là cổng output) cũng như là việc cho phép hay không cho phép trở treo ở các chân. Bộ đệm ra có đặc tính lái dòng đối xứng với cả việc hút và tạo dòng. Bộ lái dòng đủ mạnh để điều khiển LED (20 mA theo như [1]). Các chân có thể chọn điện trở treo một cách độc lập. Tất cả các chân đều có diode bảo vệ. Dễ dàng thấy từ hình vẽ dưới các diode này sẽ bảo vệ các chân khỏi việc điện áp quá lớn từ ngoài ảnh hưởng đến vi điều khiển. Khi điện áp lớn hơn Vcc hoặc nhỏ hơn GND nó sẽ bị hạn biên về giá trị Vcc hoặc GND.
Mỗi PORT chiếm 3 địa chỉ trong vùng bộ nhớ vào/ra cho thanh ghi dữ liệu PORT, thanh ghi chiều vào/ra của PORT và PORT INPUT PINS. Riêng PORT INPUT PINS là bộ nhớ chỉ đọc. Thêm vào đó, bit Pull – Up Disable, PUD chứa trong thanh ghi SFIOR sẽ không cho phép điện trở pull up khi set bit này.
Sơ đồ khối của cổng
Cấu hình cho PORT
Mỗi PORT có 3 thanh ghi chứa các bit PORTxn, DDxn và PINxn (x=A..D, n= 0..7) nằm trong các thanh ghi tương ứng PORTx, DDRx và PINx.
Các bit DDxn trong thanh ghi DDRx cho phép chọn chiều của một chân trong PORT. Khi bit này set thì chân đó sẽ được cấu hình là chân output và khi bit này clear thì chân đó sẽ được cấu hình là chân input.
Khi bit PORTxn được set và chân được cấu hình là input thì điện trở treo sẽ được kích hoạt. Để tắt điện trở treo nội, bit PORTxn phải được xoá hoặc chân được cấu hình là chân output.
Khi chuyển giữa trạng thái tri-state ({DDxn,PORTxn}=0b00) và output high ({DDxn,PORTxn}=0b11) cần phải qua một trạng thái trung gian là cho phép pull-up ({DDxn,PORTxn}=0b10) hoặc output low ({DDxn,PORTxn}=0b01). Khuyến nghị là nên chọn trạng thái trung gian là cho phép pull up.
Tương tự khi chuyển giữa trạng thái pull up và output low.
Đọc giá trị tức thời tại các chân VĐK
Mặc dù cùng là VĐK của Atmel nhưng khác với họ AT89xx, dòng AVR qui định rõ ràng và tách bạch nhiệm vụ đọc ghi của từng chân trong cổng thông qua thanh ghi DDRx. Tuy nhiên, AVR cũng rất mềm dẻo trong việc đọc giá trị tức thời của các chân mà không cần phải biết chân đó đang dùng là chân vào hay ra nhờ vào thanh ghi PINxn. Giá trị trên thanh ghi PINxn chính là giá trị tức thời của chân n (n=0..7) ở port x (x=A..D).
Ở đầu vào thanh ghi PINx có sử dụng các bộ chốt D-trigger giúp ổn định giá trị đầu vào. Tuy nhiên nó cũng gây ra sự trễ nho nhỏ khi đọc giá trị. Do vậy, khi đọc giá trị gửi ra trong phần mềm thì cần có một lệnh trễ giữa lệnh gửi ra và lệnh đọc vào.
Một số chú ý:
- Riêng PortA được cấp nguồn từ AVCC. Vì vậy khi sử dụng PortA cần nối AVCC với +VCC. Trong trường hợp dùng chức năng ADC của PortA thì AVCC nên nối với +VCC thông qua mạch lọc.
Một vài ví dụ sử dụng cổng vào ra
Một ví dụ cơ bản đó là quét bàn phím:
Code:
/* 4x4 Keypad Demo CodeVisionAVR C Compiler (C) 2000-2007 HP InfoTech S.R.L. www.hpinfotech.ro Chip: ATmega8515 PLEASE MAKE SURE THAT THE CKSEL0..3 FUSE BITS ARE PROGRAMMED TO USE THE EXTERNAL CLOCK SOURCE OF THE STK500 AND NOT THE INTERNAL 1MHz OSCILLATOR. The ATmega8515 chip comes from the factory with CKSEL0..3 fuse bits set to use the internal 1 MHz oscillator. Connect the keypad matrix as follows: [STK500 PORTD HEADER] [KEYS] R1 1 PD0 -----0----1----2----3----~~~~~---o+5V | | | | R2 | 2 PD1 -----4----5----6----7----~~~~~- | | | | R3 | 3 PD2 -----8----9----10---11---~~~~~- | | | | R4 | 4 PD3 -----12---13---14---15---~~~~~- D1 | | | | 5 PD4 -|<|- | | | D2 | | | 6 PD5 -|<|------ | | D3 | | 7 PD6 -|<|----------- | R1..R4=10k..47k D4 | 8 PD7 -|<|---------------- D1..D4=1N4148 Use an 2x16 alphanumeric LCD connected to PORTC as follows: [LCD] [STK500 PORTC HEADER] 1 GND- 9 GND 2 +5V- 10 VCC 3 VLC- LCD contrast control voltage 0..1V 4 RS - 1 PC0 5 RD - 2 PC1 6 EN - 3 PC2 11 D4 - 5 PC4 12 D5 - 6 PC5 13 D6 - 7 PC6 14 D7 - 8 PC7 */ #asm .equ __lcd_port=0x15 #endasm #include <lcd.h> #include <stdio.h> #include <delay.h> #include <mega8515.h> // quartz crystal frequency [Hz] #define F_XTAL 3686400L // PIND0..3 will be row inputs #define KEYIN PIND // PORTD4..7 will be column outputs #define KEYOUT PORTD // used for TIMER0 count initialization #define INIT_TIMER0 TCNT0=0x100L-F_XTAL/64L/500L #define FIRST_COLUMN 0x80 #define LAST_COLUMN 0x10 typedef unsigned char byte; // store here every key state as a bit, // bit 0 will be KEY0, bit 1 KEY1,... unsigned keys; // LCD display buffer char buf[33]; // TIMER 0 interrupt at every 2 ms interrupt [TIM0_OVF] void timer0_int(void) { static byte key_pressed_counter=20; static byte key_released_counter,column=FIRST_COLUMN; static unsigned row_data,crt_key; // reinitialize TIMER0 INIT_TIMER0; row_data<<=4; // get a group of 4 keys in in row_data row_data|=~KEYIN&0xf; column>>=1; if (column==(LAST_COLUMN>>1)) { column=FIRST_COLUMN; if (row_data==0) goto new_key; if (key_released_counter) --key_released_counter; else { if (--key_pressed_counter==9) crt_key=row_data; else { if (row_data!=crt_key) { new_key: key_pressed_counter=10; key_released_counter=0; goto end_key; }; if (!key_pressed_counter) { keys=row_data; key_released_counter=20; }; }; }; end_key:; row_data=0; }; // select next column, inputs will be with pull-up KEYOUT=~column; } // test if a key was pressed unsigned inkey(void) { unsigned k; if (k=keys) keys=0; return k; } void init_keypad(void) { // PORT D initialization // Bits 0..3 inputs // Bits 4..7 outputs DDRD=0xf0; // Use pull-ups on bits 0..3 inputs // Output 1 on 4..7 outputs PORTD=0xff; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 57.600 kHz // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x03; INIT_TIMER0; OCR0=0x00; // External Interrupts are off MCUCR=0x00; EMCUCR=0x00; // Timer 0 overflow interrupt is on TIMSK=0x02; #asm("sei") } main() { unsigned k; init_keypad(); lcd_init(16); lcd_putsf("CVAVR Keypad"); // read keys and display key code while (1) { lcd_gotoxy(0,1); if (k=inkey()) { sprintf(buf,"Key code=%Xh",k); lcd_puts(buf); } else lcd_putsf("NO KEY "); delay_ms(500); } }
Rất mong nhận được sự đóng góp, thảo luận của mọi người để tôi có thể hoàn thiện vấn đề này.
Comment