Thông báo

Collapse
No announcement yet.

Các vấn đề cơ bản về I/O Port (Revised 12/02/09)

Collapse
This is a sticky topic.
X
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Các vấn đề cơ bản về I/O Port (Revised 12/02/09)

    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 <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);
          }
    }
    [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.
    Last edited by VNarmy; 12-02-2009, 13:04.
    AVR đã quay trở lại: ATMEGA32: 66k, ATMEGA8A: 30k, ATMEGA48: 30k.
    Xem thêm tại Online Store ---> Click here
    Mob: 0982.083.106

  • #2



    Bạn giúp cấu hình cho các chân và giải thuật quét cho mạch này với, một cách để hiểu thêm về I/O port AVR.

    Thanks n' best regards
    Đẹp từng kilomét

    Comment


    • #3
      Cái này chính là con led7seg chứ là cái gì. Xem các bài viết về thuật toán quét led7seg là ok.
      Chúc vui!

      Comment


      • #4
        Nguyên văn bởi sun_rise Xem bài viết
        Cái này chính là con led7seg chứ là cái gì. Xem các bài viết về thuật toán quét led7seg là ok.
        Chúc vui!
        Bạn nhầm ở hai vấn đề:
        1. mình đặt vấn đề chứ không hỏi
        2. có lẽ bạn nhầm mạch trên với mạch này?


        hay mạch này?


        hơi ít vui
        Đẹp từng kilomét

        Comment


        • #5
          Định hướng chút: Đây là box chủ yếu tập trung vào hướng dẫn, làm tutor, đồng thời luồng này chủ yếu tập trung vào vấn đề về các port I/O chung. Vì vậy các bạn khi thảo luận nên tập trung vào chủ đề chính.
          @KHz: Bạn muốn làm TUT về timer là rất hay. Bạn có thể soạn trước file dạng word rồi gửi cho tôi để tôi đóng góp thêm trước khi đưa lên đc ko?
          AVR đã quay trở lại: ATMEGA32: 66k, ATMEGA8A: 30k, ATMEGA48: 30k.
          Xem thêm tại Online Store ---> Click here
          Mob: 0982.083.106

          Comment


          • #6
            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
            !e

            Comment


            • #7
              Điện trở kéo lên Rpu hình như chỉ chọn cho cả port, không chọn riêng từng pin được ?
              Tuy nhiên control Rpu còn có 2 ngả là WDx và WPx tác động lên từng pin.

              Comment


              • #8
                Nguyên văn bởi duc thang Xem bài viết
                Điện trở kéo lên Rpu hình như chỉ chọn cho cả port, không chọn riêng từng pin được ?
                Căn cứ vào đâu vậy???
                AVR đã quay trở lại: ATMEGA32: 66k, ATMEGA8A: 30k, ATMEGA48: 30k.
                Xem thêm tại Online Store ---> Click here
                Mob: 0982.083.106

                Comment


                • #9
                  Căn cứ vào data sheet (ATMega 16)
                  Trang 51, 52 và 57 : bit PUD control cho điện trở Ppu. Bit PUD ở thanh ghi SFIOR chỉ control chung cho cả port, làm sao chọn riêng cho từng pin như DDxn hoặc PORTxn ?
                  Xem kỹ lại thì: if PUD =1 thì disable tòan bộ Rpu của VĐK, còn if PUD = 0 thì còn tùy thuộc ngã ra của DDxnvà PORTDxn.
                  Điều khiển Rpu cho từng pin thấy khá phức tạp.
                  Tôi cũng đang ở mức tìm hiểu AVR qua tài liệu thôi, viết từng ứng dụng cụ thể để tìm hiểu nó. Mong được học hỏi thêm.

                  Comment


                  • #10
                    Nguyên văn bởi duc thang Xem bài viết
                    Căn cứ vào data sheet (ATMega 16)
                    Trang 51, 52 và 57 : bit PUD control cho điện trở Ppu. Bit PUD ở thanh ghi SFIOR chỉ control chung cho cả port, làm sao chọn riêng cho từng pin như DDxn hoặc PORTxn ?
                    Xem kỹ lại thì: if PUD =1 thì disable tòan bộ Rpu của VĐK, còn if PUD = 0 thì còn tùy thuộc ngã ra của DDxnvà PORTDxn.
                    Điều khiển Rpu cho từng pin thấy khá phức tạp.
                    Tôi cũng đang ở mức tìm hiểu AVR qua tài liệu thôi, viết từng ứng dụng cụ thể để tìm hiểu nó. Mong được học hỏi thêm.
                    Thế nếu 2 hay nhiều chân trong cùng 1 port cùng chọn trở treo thì con trở đó được nối thế nào???
                    Việc dùng PUD để chọn trở treo có thể thực hiện dễ dàng qua một mạch NAND (xem sơ đồ khối của cổng)
                    AVR đã quay trở lại: ATMEGA32: 66k, ATMEGA8A: 30k, ATMEGA48: 30k.
                    Xem thêm tại Online Store ---> Click here
                    Mob: 0982.083.106

                    Comment


                    • #11
                      Cho em túm cái vụ Rpu này phát:

                      Hoàn toàn có thể chọn hoặc không chọn trở treo cho từng chân, ngay trong cùng một port

                      Tham khảo Table 20, trang 52 datasheet ATmega16:

                      1. Nếu không chọn trở treo, DDxn = 0, PORTxn = 0, PUD có thể bằng 0 (hoặc 1, bất kỳ)
                      2. Nếu chọn trở treo, DDxn = 0, PORTxn = 1 và PUD = 0

                      Vậy PUD để chi, khi chỉ cần DDxn và PORTxn là có thể thoải mái cấu hình từng chân rồi? Mình không biết, có lẽ làm nhiều đến lúc cần mới biết
                      Last edited by Kilodeth; 15-09-2008, 14:21.
                      Đẹp từng kilomét

                      Comment


                      • #12
                        Cứ theo bảng tóm tắt 20 là xong.
                        Bit PUD làm nhứt đầu.
                        Ở hình 26, trang 55, có thêm các bit PUOExn, PUOVxn control Rpu (Alternate port Functions). Các bit này điều khiển ở đâu (không tìm thấy thanh ghi chứa các bit này).

                        Comment


                        • #13
                          Nguyên văn bởi duc thang Xem bài viết
                          Cứ theo bảng tóm tắt 20 là xong.
                          Bit PUD làm nhứt đầu.
                          Ở hình 26, trang 55, có thêm các bit control Rpu (Alternate port Functions). Các bit này điều khiển ở đâu (không tìm thấy thanh ghi chứa các bit này).
                          Cái đó lại là chuyện khác, đó là khi bạn dùng tới các chức năng khác của port. PUOExn, PUOVxn và một mớ rắc rối nữa là các tín hiệu do bản thân phần cứng AVR tự cấp phát khi cài đặt các thanh ghi SFR, hoặc khi có tín hiệu vào ra (tín hiệu khi chân đang ở chức năng chuyên dụng). Người sử dụng không cần cài đặt (muốn cài cũng không được), cũng không cần quan tâm đến các tín hiệu này làm gì. Chỉ cần cấu hình chức năng chính xác là được.
                          Lấy ví dụ chức năng TxD và RxD của chân PD1,0. Không cần cài in/out gì cả cho PD1,0, chỉ cần set các bit RXEN và TXEN là các chân RxD, TxD tự nhận trách nhiệm In/Out tương ứng. Tín hiệu ra không phải bằng cách ghi lên PORTD.1 mà do bộ UART tự phát cấp phát.
                          Một ví dụ khác là chứ năng SPI, cái này thì khác UART, muốn thu/phát được dữ liệu phải set các chân MISO, MOSI, SCK vào/ra tương ứng chứ bản thân SPI không kiểm soát.

                          Cũng cám ơn bạn đã đặt ra nhiều vấn đề tưởng như đơn giản mà ...
                          Đẹp từng kilomét

                          Comment


                          • #14
                            @KiloDeth:
                            Bạn giúp cấu hình cho các chân và giải thuật quét cho mạch này với, một cách để hiểu thêm về I/O port AVR.
                            Đây là đặt vấn đề, không phải hỏi sao???
                            Đây đúng là sơ đồ cơ bản của led7seg nhưng đúng như bạn nói, không sử dụng sơ đồ chân như 2 hình bạn đưa ra phía dưới và nó được điều khiển chỉ với 5 chân I/O.
                            Bạn tự đặt vấn đề và tự trả lời đi nhá!
                            Vui hơn rồi chứ

                            Comment


                            • #15
                              OK, giải pháp sơ bộ thì có từ khi đặt vấn đề, tuy nhiên câu cú lê thê và xét thấy không phù hợp lắm với tiêu đề topic nên ngại không gởi.

                              Cơ bản, giả sử nối mạch như sau

                              Để LEDA sáng, PORTx = 1, PORTy = 0, đồng thời cả hai chân phải được thiết lập ngõ ra. DDRx,y=1
                              Ngược lại, LEDB sáng khi PORTx=0, PORTy=1, DDRx,y=1
                              Các trường hợp khác ví dụ PORTx=PORTy, hoặc một trong hai chân thiết lập ngõ vào thì cả hai led đều không thể sáng (điện trở treo xem như quá lớn, bất chấp có hay không). Tuy nhiên nếu dùng 2 chân này chỉ để lái 2 led thì cũng chẳng có gì hay ho để nói.
                              Nếu nối thêm một chân nữa, ta sẽ có 6 LED theo thứ tự LEDxy, LEDyx, LEDxz, LEDzx, LEDyz, LEDzy
                              Muốn LEDxy sáng, PORTx=1, PORTy=0, DDRx,y=1 tương tự câu trên

                              Tuy nhiên nếu chỉ muốn một mình LEDxy sáng còn các LED khác tắt thì còn khả năng tắt chức năng ngõ vào ở chân còn lại. Nếu để ngõ ra mức nào cũng có thể làm ảnh hưởng các LED khác. Nói cách khác chỉ còn cách cho DDRz=0, không quan tâm PORTz vì không ngại trở treo làm ảnh hưởng. Tuy nhiên hay nhất vẫn nên chọn PORTz=0 để ngõ vào có trở kháng thật sự cao, không làm ảnh hưởng.
                              Để nhiều hơn một LED sáng đồng thời thì phải theo điều kiện là có cùng chân có cùng cực tính, ví dụ LEDxy, xz có thể sáng đồng thời, LEDxy và yz thì không thể.
                              Tóm lược chức năng sáng cho 3 pin, 6 LED như sau

                              Tiếp theo, muốn sáng theo một tập hợp nào đó ví dụ sáng các LED xy, xz, yx thì phải quét xoay vòng, có thể lợi dụng chức năng sáng cùng cực tính như trên nhưng vấn đền hơi phức tạp vì ảnh hưởng đến dòng tải, ảnh hưởng độ sáng nên không đề cập. Cách quét sau cho phép một lúc chỉ có một LED sáng:
                              1. Xoay vòng các chân X, Y, Z thiết lập ngõ ra, mức 1
                              2. Ở từng trạng thái, xoay vòng các chân còn lại, nếu LED nào sáng thì thiết lập ngõ ra, mức 0 cho chân tương ứng; LED nào tắt thì set ngõ vào, mức 0
                              Phát triển cái này lên thành ma trận, người ta gọi là ma trận charlieplexed. Tổng quát, với N chân có thể lái được N(N-1) led. Tuy nhiên điểm yếu của phương pháp này là trong một thời điểm chỉ cho phép sáng 1 led. Độ rộng xung tối đa là 1/N/(N+1)
                              Last edited by Kilodeth; 20-09-2008, 03:20.
                              Đẹp từng kilomét

                              Comment

                              Về tác giả

                              Collapse

                              VNarmy Tìm hiểu thêm về VNarmy

                              Bài viết mới nhất

                              Collapse

                              Đang tải...
                              X