Các vấn đề cơ bản về I/O Port

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:



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

#include

#include

#include

 

// 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);

      }

}

 





tóm lại




Sau khi coi lại về cổng port của AVR thì zemen thấy cần nhớ các điểm chính:
- Dùng 3 thanh ghi cho mỗi port, gồm th.ghi chiều (DDR), th.ghi mức (PORT) và th.ghi đọc (PIN)
- Port có chức năng Read-Modify-Write (Đọc-sửa-xoá)
- Có thể xử lí từng chân của port (tức là xử lí từng bít của các thanh ghi)
- Mỗi chân của port có thể có các trạng thái vào, ra và tổng trở cao tuỳ theo giá trị của 3 thanh ghi port
- Các chân của port đều có diode bảo vệ quá áp
- Có thể chọn điện trở kéo lên Rp cho từng chân của port
- Dòng cấp và nhận cho mỗi chân đạt đến 20mA
- 1 chân vdk, khi dùng các chức năng khác nếu có (như ADC, SPI,...) thì không dùng đc chức năng vào/ra bình thường của port




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.

Post a Comment

Mới hơn Cũ hơn