Thông báo

Collapse
No announcement yet.

Kết nối PIC với EEPROM, DS1307 dùng chuẩn I2C ( hardware)

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Kết nối PIC với EEPROM, DS1307 dùng chuẩn I2C ( hardware)

    Part1: Giới thiệu

    Đã làm việc với VĐK và cụ thể là PIC chúng ta không thể không biết đến chuẩn I2C, đã có rất nhiểu bài viết nói vè cái này, nhưng một cách chi tiết cụ thể mình chưa thấy. Nhân ngày chủ nhật rỗi rãi xin trình bày với các bạn về chuẩn I2C


    I2C: Viết tắt của Inter-Intergrated Circuit – là một bus nối tiếp do Philip phát triển. Trước đây I2C chủ yếu được dùng trong việc chế tạo các hệ thống điện tử sử dụng chip của Philip. Ngày nay I2C được sử dụng rộng rãi trong việc kết nối các thiết bị ngoại vi tốc độ thấp vào các mạch tích hợp. I2C sử dụng 2 đường truyền tín hiệu 2 chiều (một đường clock và một đường data), sử dụng hiệu điện thế 5V và cùng được kéo lên cao(pull-ups) bằng điện trở. I2C hoạt động theo nhiều mode: mode chuẩn (standard mode) hoạt động ở tốc độ 100kbit/s, mode tốc độ thấp (low-speed mode) hoạt động ở tốc độ 10kbit/s. Tần số clock có thể cho xuống 0. I2C có sử dụng 7 bit để định địa chỉ, do đó trên một bus có thể có 112 nút (16 địa chỉ được sử dụng vào mục đích riêng). Điểm mạnh của I2C là ở chỗ, một vi điều khiển có thể dùng để điều khiển cả một mạng thiết bị mà chỉ tốn 2 chân của vi điều khiển.
    ( đoạn này của anh Peacemoon bên tư đông hoá . em thây hay quá nên xài tạm )

    Chính vì nguyên nhân đó mà I2C cùng với SPI là hai chuẩn giao tiếp được sử dụng nhiều nhất trong các IC đặc biệt là các VĐK 8 bít.
    Last edited by mafd_47; 09-11-2005, 07:35.
    Dia chi cho cac Fan muon hoc Pic

  • #2
    Part2: I2C trong VĐK PIC

    Ở Việt Nam Pic16x được sử dụng và đề cập nhiều nhất là 16F84, 16F628, 16F88, 16F87x. Trong đó:

    + 16F84, 16F628 ko tích hợp chuẩn I2C
    + 16F88 tích hợp I2C nhưng chỉ hỗ trợ chế độ Slave, ko dùng đựoc chế độ Master
    + 16F87x tích hợp I2C cả chế độ Master và Slave

    Do đó nếu muốn sử dụng chế độ Master với các chíp 16F84, 16F628, 16F88 chúng ta phải gây dựng bằng phần mềm, và đã có rất nhiều tài liệu đề cập đến cái này điển hình nhất là Tutorial6 của ngài Nigel, các bạn hoàn toàn có thể tham khảo, hay copy paste code về I2C vào trong chương trình của mình, ( cũng ko nên tự gây dựng nấy vì nếu tự làm sẽ mất rất nhiều thời gian, và ko tối ưu )

    Còn với 16F87x để sử dụng chuẩn giao tiếp I2C ta có thể dùng Hardware đã được tích hợp sẵn. Cái này dùng đơn giản hơn, code ngắn hơn và tối ưu hơn so với việc sử dụng Software. Do đó trong Project này tôi chỉ xin đề cập đến việc dùng I2C trong 16F87x

    Bà con nào đã làm rồi thì xin đóng góp ý kiến, còn chưa làm bao giờ có thể tham khảo Project này
    Dia chi cho cac Fan muon hoc Pic

    Comment


    • #3
      Part3: Cách sử dụng I2C chế độ Master trong 16F87x

      Trong Pic 16F87x có 3 thanh ghi điều khiẻn quá trình truyền và nhận dữ liệu: đó là SSPSTAT ( 94h bank 1), SSPCON1 ( 14H bank 0) và SSPCON2 ( 91H bank 1 ). Trong đó thì:
      SSPSTAT:
      + SMP chọn Speed chuẩn ( =1: 100Kb, 1Mb, =0: 400Kb)
      + CKE
      + R/W báo rằng quá trình truyền vẫn đang diễn ra
      + BF báo rằng SSPBUF vẫn đang đầy ( trong cả hai trường hợp transmit, Receive )




      SSPCON1
      + WCOL : báo rằng có sự xếp chông dữ liệu
      + SSPEN enable chế độ I2C
      + SSPM3:SSPM0: chọn chế độ với chế độ I2C master: là '1000'




      SSPCON2
      + ACKSTAT: Bít ACK được nhận từ Slave ( =0, chỉ dùng trong Transmit )
      + ACKDT, ACKEN: dùng để phát bít ACK hay NACK từ Master ( trong chế độ Receive ACKDT =0 là ACK, =1 là NACK )
      + RCEN : tín hiêu báo hiệu quá trình nhân ( chỉ dùng trong Receive: khi RCEN = 1, Master nhận tín hiệu từ Slave )
      + PEN, RSEN, SEN: bit khởi tạo quá trình truyền Stop, Restart, Start




      Để điều khiển tốc độ baud của chế độ, người ta sử dụng thanh ghi SSPADD.
      I2C làm việc ở 3 chế độ chuẩn ( tất nhiên chỉ tương đối ) : 100Kb, 400Kb, 1Mb. Nếu ta dùng thạch anh 4M, và cần sử dụng tốc độ 100Kb ta phải nạp giá trì vào thanh ghi SSPADD là: 28H với tốc độ 400Kb ta cần giá trị là 0AH. ( Tham khảo trang 97 Table 9-17( datasheet ) để biết thêm chi tiết )

      Còn để lưu và nhận dữ liệu người ta dùng thanh ghi SSPBUF

      Như vây tổng cộng có cả thẩy 5 thanh ghi được dùng đến : SSPSTAT, SSPCON1, SSPCON2 ( chọn chế độ và đỉều khiển đường truyền ) SSPADD ( khởi tạo tốc độ Baud ) và SSPBUF dùng để lưu dữ liệu trong hai quá trình Receive, và Transmister.

      1. Hàm Initialize của I2C
      Cũng tương tự như khi dùng USART, LCD, PWM... đầu tiên ta phải khởi tạo các giá trị ban đầu của chúng. Chúng ta nên tách riêng việc khởi tạo này thành một chương trình con. Việc khởi tạo phải theo các bước như sau:
      + Chọn chế độ Master mode bằng việc SSPM3 : SSPM0 = 1000
      + Enable MASTER mode: SSPEN = 1
      + Chọn baud chuẩn với 100Kb thì SMP =1
      + Set tốc độ baud của đường truyền: với 100Kb thì SSPADD = 28H
      Dưới đây là chương trình cho Initialize cho I2C:

      INIT_I2C
      PHP Code:
      INIT_I2C
            
              BANKSEL SSPCON            
      chon MSSP che do MASTER MODE
              MOVLW   
      B'00101000'       Thiet lap MSSP cho chan PORTC o che do I2C
              MOVWF   SSPCON            
      SSPENSSPM BITS

              BANKSEL SSPSTAT         
      Thiet lap che do chuan cua toc do baud SMP
              
      MOVLW   B'10000000'       Toc do chuan la 100Kb
              MOVWF   SSPSTAT    

              BANKSEL SSPADD            
      Thiet lap toc do duong truyen
              MOVLW   0x28                   
      Tinh toan gia tri cho thiet lap toc do
              
      MOVWF   SSPADD
             
              RETLW   0x00 

      2. Truyền các bít chức năng
      Để truyền và nhận dữ trong I2C tạo thành khối người ta dùng các bít Stop, Start, Restart, ACK, NACK ( ứng với PEN, SEN, RSEN, ACKEN, ACKDT ), Ý nghĩa của các bít này các bạn tham khảo các tài liệu nói về chuẩn I2C, tôi sẽ ko đề cập ở đây ( vì nói cái này sẽ rất dài dòng ) mà ở đây tôi chỉ đề cập đến hoạt dộng của các bít này trong PIC16F87x
      (http://users.pandora.be/educypedia/electronics/I2C.htm I2C chuẩn tham khảo tại đây )

      Quá trình hoạt động các bit này khá giống nhau đẻ bắt đầu phát đi: ta phải set bit tưong ứng trong thanh ghi đó đi VD muốn gửi bít Stop đi ta chỉ cần PEN =1, tưong tự với các bit kia và khi đã truyền xong rồi thì các bit đó sẽ tự động chuyển về 0 ( băng Hardware) .

      Nhưng các bạn phải lưu ý qúa trình hoạt động này chỉ có tác dụng khi mà I2C đã hoàn thành xong nhiệm vụ trước đó. Như vậy ta cần phải biết được lúc nào chương trình đa hoàn thành xong nhiệm vụ và đây chính là vai trò của cờ SSPIF trong thanh ghi PIR1. Cũng tương tự như các cơ khác, SSPIF báo cho uC biét là hoạt động đã kết thúc bằng cách Set từ 0 lên 1 và ta phải xoá cờ này băng phần mềm cho các hoạt động tiếp theo. Do yêu cầu I2C là: khi hoạt động này kết thúc, thì mới cho phép hoạt động kia bắt đầu chính yêu cầu này đặt ra chúng ta phải có một chương trình con báo hiệu khi kết thúc một hoạt động. Như vậy chương trình con đó là:

      WaitMSSP
      PHP Code:
      WaitMSSP
              BANKSEL PIR1
              BTFSS   PIR1
      ,SSPIF        Kiem tra trang thai co
              
      GOTO    $-1                      Chua hoan thanh
              BCF     PIR1
      ,SSPIF           Da hoan thanh cong viecluc nay co the chuyen sang hoat dong moi
              RETLW   0 
      Chương trình con này sẽ được dùng rất nhiều về sau trong mọi hoạt động của chương trình, và ta nên đặt chương trình con này ở cuối các hoạt động

      Như vậy kết hợp với chương trình con WaitMSSP chúng ta có các hoạt động: phát ra bit Stop, Restart.... là

      STARTBit
      PHP Code:
      STARTBit
              BANKSEL SSPCON2
              BSF     SSPCON2
      ,SEN       Gui bit start 
              CALL    WaitMSSP   
              RETLW   0x00 
      RESTARTBit
      PHP Code:
      RESTARTBit
              BANKSEL SSPCON2
              BSF     SSPCON2
      ,RSEN      Gui bit restart 
              CALL    WaitMSSP   
              RETLW   0x00 
      STOPBit
      PHP Code:
      STOPBit
              BANKSEL SSPCON2           
      Gui va kiem tra bit Stopdoi cho den khi hoan thanh xong
              BSF     SSPCON2
      ,PEN       gui bit Stop
              CALL    WaitMSSP
              RETLW   0x00 
      ACKBit
      PHP Code:
      ACKBit
              BANKSEL SSPCON2
              BCF     SSPCON2
      ,ACKDT     Chon gui ACK
              BSF     SSPCON2
      ,ACKEN     Gui
              CALL    WaitMSSP
              RETLW   0x00 

      NACKBit

      PHP Code:
      NACKBit
              BANKSEL SSPCON2
              BSF     SSPCON2
      ,ACKDT     Chon gui NACK
              BSF     SSPCON2
      ,ACKEN     Gui di
              CALL    WaitMSSP
              RETLW   0x00 

      ( Lưu ý có sự khác biệt một chút trong bit ACKBit, NACKBit là : ACKEN gửi bit ACK nói chung đi, còn ACKDT dùng để chọn bit gửi đi là ACK ( = 0 ) hay NACK ( =1 ) )


      3. Truyền và nhận dữ liệu trong I2C
      Bước tiếp theo tôi xin đề cập đến cách truyền và nhận dữ liệu trong PIC16F87x.

      Các bạn phải phân biệt được cách chuyền và nhận ( tưong ứng với Transmit, Receive ) trong PIC

      Để bắt đầu nhận dữ liệu từ Slave thì Master phải set bít RCEN = 1 , cũng tương tự như các bít Stop, Start,..... đây cũng là một hoạt động, sau khi bit cuối cùng đuợc nhận thì RCEN sẽ tự động Clear, do đó để bắt đầu quá trình Read tiếp theo thì ta phải tiép tục Set RCEN ( đối với quá tình đọc ít nhất hai lần trở lên ) đê tiếp tục một hoạt động mới. Ta có module nhận dữ liệu

      READ_I2C
      PHP Code:
      READ_I2C                          ;  Bit gui la cuoi la ACK
              BSF     STATUS
      ,RP0
              BTFSC   SSPSTAT
      ,2             kiem tra bit R/Wxem qua trinh Transmit da ket thuc chua
              
      GOTO    $-1
              BANKSEL SSPCON2
              BSF     SSPCON2
      ,RCEN      cho phep RECEIVE Mode (I2C )
              
      CALL    WaitMSSP              Doi cho den khi I2C thuc hien xong TUC LA NHAN DU LIEU XONG
              CALL    ACKBit                    
      Gui bit ACK toi Slave
              BANKSEL SSPBUF 
              MOVF    SSPBUF
      ,W           Luu du lieu vao W
              
      RETURN 
      Trong module này tôi có dùng bít R/W ở thanh ghi SSPSTAT, bít này cho sự đảm bảo rằng quá trình Transmit đã kết thúc và sẵn sàng cho hoạt động Receive, Khi qua trình truyền kết thúc ta gửi bit ACK tới Slave báo cho Slave biết rằng Master sẵn sàng nhận dự liệu tiếp theo


      Còn quá trình truyền dữ liệu là ngay sau khi chúng ta ghi dữ liệu cần truyền lên thanh ghi SSPBUF tất nhiên, quá trình Transmit bắt đầu ( lúc bắt đầu ghi dữ liệu lên SSPBUF ) khi các hoạt động trước nó đã kết thúc. Vậy ta có module truyền là

      WRITE_I2C
      PHP Code:
      WRITE_I2C                                 In la W
              BANKSEL SSPBUF            
      Chon bank0
              MOVWF   SSPBUF            

              
      CALL    WaitMSSP
              RETLW   0x00 
      Tài liệu về I2C tham khảo ở :http://ww1.microchip.com/downloads/en/DeviceDoc/i2c.pdf

      Và đến đây chúng ta đã có đủ các công cụ cho việc tryền hay nhận dữ liệu dùng I2C rôi đấy
      Last edited by mafd_47; 15-11-2005, 07:55.
      Dia chi cho cac Fan muon hoc Pic

      Comment


      • #4
        Part 4. Truyền và nhận dữ liêu qua EEPROM 24C01C

        1.) Quá trình truyền một byte dữ liệu từ Master qua slave ( transmit )
        Nguyên tắc truyền một byte dữ liệu gồm có các bước cơ bản sau ( tôi ko muốn đề cập đến việc dùng ngắt để xử lý vì rất phức tạp )
        + Gửi bít Start từ Master tới slave. Đợi cho đến khi truyền xong
        + Gửi địa chỉ của slave lên đường truyền. Dùng để chọn Slave nào hoạt động, Đợi cho đến khi truyền xong
        + Gửi địa chỉ cần lưu dữ liệu tới . Đợi cho đên khi truyền xong
        + Gửi dữ liệu cần truyền tới Slave, đợi cho đến khi truyền xong
        + Tiếp tục gửii dữ liệu ......
        + Khi muốn kết thúc gửi bít Stop lên đường truyền

        Tuân thủ lần lượt các bước trên ta có module truyền dữ liệu của một byte

        WRITE_BYTE
        PHP Code:
        WRITE_BYTE                               IN ACCdulieuADDR dia chi can truy nhap
                CALL    STARTBit

                MOVLW   ADDR_RTC          
        Load CONTROL BYTE (input mode)
                
        CALL    WRITE_I2C

                MOVF    ADDRESS
        ,W          Xuat du lieu ra I2C
                CALL    WRITE_I2C

                MOVF    ACC
        ,W                    Xuat du lieu ra I2C
                CALL    WRITE_I2C
         
                CALL    STOPBit
                RETLW   0x00                       
        Ket thuc qua trinh viet du lieu len EEPROM 
        Lưu ý : ADDR_RTC là địa chỉ của Slave và bit 0 của nó phải bằng 0 ( báo cho Slave biết quá trình là Write ) còn ACC là biến dữ liệu, ADDRESS là biến đia chỉ ta cần dưa vào
        Như vậy để truyền một byte dữ liệu là 0x15 có địa chỉ 0x05 tới EEPROM 24C01C ( địa chỉ 0xA0 ) ta chỉ việc
        ADDR_RTC EQU 0xA0 ; Dia chi
        ADDRESS EQU 0x20 ; Khai bao Ram
        ACC EQU 0x21
        .............................

        MOVLW 0x06
        MOVWF ADDRESS
        MOVLW 0x15
        MOVWF ACC
        CALL WRITE_BYTE
        2. Quá trình nhận dữ liệu từ slave
        Quá trình nhận dữ liệu từ slave phải tuân thủ theo các bước sau
        + Gửi bít Start từ Master tới slave. Đợi cho đến khi truyền xong
        + Gửi địa chỉ của slave( bit 0 = 0 ) lên đường truyền. Dùng để chọn Slave nào hoạt động, Đợi cho đến khi truyền xong
        + Gửi địa chỉ của dữ liệu càn nhận Đợi cho đên khi truyền xong
        + Gửi bit Restart, Đợi cho đến khi truyền xong
        + Gửi địa chỉ của slave lên đường truyền ( bit 0 =1 báo rằng hoạt động sắp tới là đọc). Đợi cho đến khi truyền xong
        + Đọc dữ liệu từ Slave . Đợi cho đên khi đọc xong
        + Phát bít ACK báo tiếp tục nhận dữ liệu . Đợi cho đên khi truyền xong
        + Đọc dữ liệu từ Slave . Đợi cho đên khi đọc xong
        + Phát bít ACK báo tiếp tục nhận dữ liệu . Đợi cho đên khi truyền xong
        ...................
        + Đọc dữ liệu từ Slave . Đợi cho đên khi đọc xong
        + Phát bít NACK báo rằng qua trình nhận dữ liệu đã kết thúc . Đợi cho đên khi truyền xong
        + Phát bít Stop để kết thúc

        Tuân thủ theo các bước trên ta sẽ có quá trình đọc một byte dữ liệu

        READ_BYTE
        PHP Code:
        READ_BYTE                         In:ADDRESS (dia chi can truy nhap),  OUTW
                CALL    STARTBit          
        Gui bit Start

                MOVLW   ADDR_RTC          
        Load CONTROL BYTE  cho viec xuat du lieu )
                
        CALL    WRITE_I2C

                MOVF    ADDRESS
        ,W         Load ADDRESS Byte vao W
                CALL    WRITE_I2C
            
                CALL    STOPBit                 
        Qua trinh restart
                CALL    STARTBit
               
                MOVLW   AĐR_RTC
                ADDLW    0x01                  
        Set bit 0 1 bao cho slave biet qua trinh doc lan sau
                CALL    WRITE_I2C
          
                BSF     STATUS
        ,RP0
                BTFSC   SSPSTAT
        ,2         kiem tra bit R/W
                
        GOTO    $-1

                BANKSEL SSPCON2
                BSF     SSPCON2
        ,RCEN      Cho phep RECEIVE Mode (I2C )          ; loi o cho nay day
                CALL    WaitMSSP

                BCF     STATUS
        ,RP0
                BCF     PIR1
        ,SSPIF
               
                CALL    NACKBit           
        Phat bit NACKBit ket thuc qua trinh nhan
                
                CALL    STOPBit

                BANKSEL SSPBUF            

                
        MOVF    SSPBUF,W          Luu gia tri doc duoc tu EEPROM vao thanh ghi W

                
        RETURN                    ; Ket thuc qua trinh doc ghi 
        Như vây để đọc giá tri tại địa chỉ 0x05 của EEPROM 24C01C và lưu dũ liệu vào thanh ghi ACC ta làm như sau

        MOVLW 0x05
        MOVWF ADDRESS
        CALL READ_BYTE
        MOVWF ACC
        Thật đơn giản phải ko nào. Bây gờ các bạn có thể hoàn toàn tự truy suất EEPROM một cách vô tư, tuỳ ý rồi đây
        Last edited by mafd_47; 09-11-2005, 07:39.
        Dia chi cho cac Fan muon hoc Pic

        Comment


        • #5
          Part 5 Giao tiếp với DS1307 real time clock dùng PIC16F877A

          DS1307 là con Real Time clock của háng Dallas và đựoc dùng khá phổ biến hiện nay. Cũng giống như các RTC khác, DS1307 có các đặc tính sau:

          + Dùng chuẩn giao tiếp I2C
          + Hiển thị giờ phút giây, ngày, tháng năm ( đến tận năm 2099 )
          + Dùng thạch anh 32.768 Khz làm bộ tạo dao động
          + Sử dụng Pin 3V đề đề phòng khi mất điện
          + Có 56 byte Ram cho người sử dụng......

          Một số đặc điểm mà các bạn cần phải chú ý:

          + DS1307 có 7 byte dữ liệu nằm từ địa chỉ 0x00 tới 0x06, 1 byte điểu khiển, và 56 byte lưu trữ ( dành cho người sủ dụng )
          + Khi xử lý dữ liệu từ DS1307, họ đã tự chuyển cho ta về dạng số BCD, ví dụ như ta đọc đựoc dữ liệu từ địa chỉ 0x04 ( tưong ứng với Day- ngày trong tháng) và tại 0x05 ( thang ) là 0x15, 0x11 như thế có nghĩa là lúc đó là ngày 15-11 chứ ko phải là ngày 21 tháng 17
          + Lưu ý đến vai trò của chân SQW/OUT. Đây là chân cho xung ra của DS1307 có 4 chế độ 1Hz, 4.096HZ, 8.192Hz, 32.768Hz... các chế độ này đuợc quy định bởi các bít của thanh ghi Control Register ( địa chỉ 0x07 )
          + Địa chỉ của DS1307là 0xD0

          Quay lại với phần trên có lẽ có nhiều bạn thắc mắc tại sao chúng ta có thể đọc ghi một cách liên tục và địa chỉ sẽ như thế nào. Thật đơn giản địa chỉ truy suất phụ thuộc vào dịa chỉ ban đầu mà ta đưa vào, và sau mỗi lần đọc/ghi dữ liệu, biến địa chỉ này tự động tăng lên một cho đến khi gặp bit Stop thì thôi

          Trở lại với Project của chúng ta biến chúng ta cần truy suất ở đây cả thảy có 7 địa chỉ đó là Second, minute, hour, Day, Date, Month, year vây ta cần phải đặt 7 biếnt dữ liệu trong chương trình trong việc xử lý dữ liệu. Sau đây là đặt biền và chương trình con để đọc dữ liệu từ RTC

          PHP Code:
                  CBLOCK     0x20            ;start of general purpose registers
                      ADDRESS
                      ACC
                      RAM_SEC      
                      RAM_MIN       
                      RAM_HOU       
                      RAM_DAY       
                      RAM_DAT       
                      RAM_MON       
                      RAM_YEA       
               endc

          ;.................................
          main:

          ;.................................
          READ_RTC                          
                  CALL    STARTBit          
          Gui bit Start

                  MOVLW   ADDR_RTC          
          Load CONTROL BYTE  cho viec xuat du lieu )
                  
          CALL    WRITE_I2C

                  MOVLW   0x00              
          Load ADDRESS Byte vao W
                  CALL    WRITE_I2C
           
                  CALL    STOPBit
                  CALL    STARTBit

                  MOVLW   0xD1           
          Chon che do dia chi read
                  CALL    WRITE_I2C     

          Qua trinh doc du lieu tu RTC
                  CALL    READ_I2C           
          Doc du lieu SECOND
                  MOVWF   RAM_SEC
                  CALL    READ_I2C           
          Doc du lieu MINUTE
                  MOVWF   RAM_MIN
                  CALL    READ_I2C           
          Doc du lieu HOUR
                  MOVWF   RAM_HOU   
                  CALL    READ_I2C           
          Doc du lieu DAY
                  MOVWF   RAM_DAY
                  CALL    READ_I2C           
          Doc du lieu DATE
                  MOVWF   RAM_DAT
                  CALL    READ_I2C           
          Doc du lieu MON
                  MOVWF   RAM_MON
                  BSF     STATUS
          ,RP0
                  BTFSC   SSPSTAT
          ,2         kiem tra bit R/W
                  
          GOTO    $-1
                  BSF     SSPCON2
          ,RCEN      Cho phep RECEIVE Mode (I2C )
                  
          CALL    WaitMSSP          Doi cho den khi I2C thuc hien xong TUC LA NHAN DU LIEU XONG
                  CALL    NACKBit           
          Bao cho SLAVE ket thuc qua trinh nhap du lieu
                  BANKSEL SSPBUF
                  MOVF    SSPBUF
          ,W
                  MOVWF   RAM_YEA           
          Doc du lieu year du lieu cuoi cung )
                  
          CALL    STOPBit
                  RETLW   0x00              
          Ket thuc qua trinh doc ghi 

          Với chương trình đọc dữ liệu trên ta hoàn toàn có thể truy suất vào RTC bất cứ lúc nào để biết giờ hệ thống. Nhưng ở đây lại có một bài toán đặt ra là nên chọn thời điểm nào để đọc hay đọc như thế nào. Những bạn nào đã từng làm về RTC chăc hẳn đã gặp phải trường hợp là kim giây nhảy lung tung lên, tôi nghĩ rằng nguyên nhân ở chỗ các bạn đọc dữ liệu một cách liên tục. Rõ ràng cách này là hoàn toàn ko tối ưu. Giải pháp như thế nào cho hợp lý

          Các bạn có thấy vai trò của chân SQW ở trên ko, nó co một chế độ phát xung 1Hz như vậy nếu như ta nối chân này với một trở 4,7K kéo lên nguồn và mắc vào chân RB0 của PIC16F877, và cho sử dụng ngắt ngoài Như vậy cứ mỗi khi có ngắt thì ta mới tiến hành đọc dũ liệu. Tôi nghĩ cách này rất tối ưu, vì trong thời gian chờ đợi ta có thể làm việc khác chứ ko phải là chỉ có việc đọc dữ liệu từ RTC. Như vậy chương trình ngắt sẽ là

          movwf w_temp ; save off current W register contents
          movf STATUS,w ; move status register into W register
          movwf status_temp ; save off contents of STATUS register
          movf PCLATH,w ; move pclath register into w register
          movwf pclath_temp ; save off contents of PCLATH register

          ;************************************************* *****************************
          ;
          BTFSS INTCON,1
          GOTO NEXT_INTERUPT
          BANKSEL PORTB
          CALL READ_RTC
          ; Qua trinh xu ly du lieu va xuat du lieu tai day
          BCF INTCON,1
          NEXT_INTERUPT
          ;************************************************* *****************************

          movf pclath_temp,w ; retrieve copy of PCLATH register
          movwf PCLATH ; restore pre-isr PCLATH register contents
          movf status_temp,w ; retrieve copy of STATUS register
          movwf STATUS ; restore pre-isr STATUS register contents
          swapf w_temp,f
          swapf w_temp,w ; restore pre-isr W register contents
          retfie ; return from interrupt


          Việc xử lý dữ liệu và truy suất dữ liệu bằng Led 7 đoạn, hay bằng LCD như thế nào là tuỳ thuộc ở các bạn. Có thể tham khảo VD dưới đây về RTC

          Chúc các bạn sẽ tự tạo đựoc cho mình một chiếc đồng hồ hiển thị ngày giờ cho chính gia đình mình

          Thân ái !!!

          Nhóm Picvietnam
          Last edited by mafd_47; 11-11-2005, 23:47.
          Dia chi cho cac Fan muon hoc Pic

          Comment


          • #6
            Quả thực đây là bài viết rất cơ bản, rất có ích. Em bình chọn cho luồng này của bác mafd_47.

            Comment


            • #7
              Em cũng bình chọn cho luồng này của bác mafd_47.
              -------------------

              Comment


              • #8
                Bài này viết được đấy, tui chỉ có một điều góp ý, bạn code bằng C cho anh em dễ đọc, đọc ASM của mấy thằng PIC lâu lắm.

                Comment


                • #9
                  DS1337 + PIC Viết bằng CCS

                  Bác Mạnh viết rất hay và cơ bản, ai mới học chắc đêu làm được. Đúng là viết bằng ASM thì hơi khó đọc cho ai mới làm để hiểu tương tận vấn đề, nhưng đừng vì thê mà nản. Tôi thấy ASM thật sự rất hay, no giúp ta hiêu tường tận về phần cứng hơn.
                  - Việc giao tiếp với ngoại vi bên ngoài là một bài toán không thể thiếu khi làm việc với VDK. Rât may là PIC đã hỗ trợ khá nhiều về phần cứng, ta chi việc gọi ra và dùng nó. Về giao tiếp với các IC thời gian thực bằng I2C, trước đây tôi đã làm và dùng bằng CCS. Phai công nhận là viết bẳng CCS công việc dễ dàng hơn nhiều. Vấn đề chính của ta chi còn là việc tôi ưu phần mềm. Tôi định đưa code mà tôi đã viết lên nhưng tiếc là hôm nay trong USB chỉ có bản demo thôi, anh em thông cảm, hôm khăc sẽ pót sau. Trong bản chính có đầy đủ cả phần bàn phím chỉnh và hẹn giờ gồm 4 phím giống như ở đồng hồ Casio mà bạn hay dùng ý (mode - change - alarm set - clear status).
                  Chi tiết về lập trình bạn có thể xem thêm trong datasheet của con DS1337 đề hiểu quá trình gửi lệnh.
                  Chuc thành công!
                  here is demo:

                  #include <16F877A.h>
                  #include <def_877a.h>
                  #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
                  #use delay(clock=20000000)
                  #use rs232(baud=4800,parity=N,xmit=PIN_C6,rcv=PIN_C7,bi ts=9)
                  #use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3)

                  int8 sec,min,hour,dow,date,month,year;
                  int8 const maled7[10] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90 };
                  int8 so[13]; //sec,min,hour,dow,date,month,year
                  #bit key = 0x06.7
                  void display() {
                  int8 led1,led2,led3,led4,led5,led6,led7,led8,led9,led10 ,led11,led12;
                  int8 temp1,temp2;
                  i2c_start();
                  i2c_write(0xD0);
                  i2c_write(0x00); //set register pointer
                  I2C_start();
                  I2C_write(0xD1); /* write slave address + read */
                  sec = i2c_read(1); /* starts w/last address stored in register pointer */
                  min = i2c_read(1);
                  hour = i2c_read(1) & 0x3F;
                  dow = i2c_read(1);
                  date = i2c_read(1);
                  month = i2c_read(1) & 0x7F;
                  year = i2c_read(0);
                  i2c_stop();
                  led1=sec & 0x0F;
                  led2=(sec & 0xF0)>>4; //convert to BCD SEC
                  led3=min & 0x0F;
                  led4=(min & 0xF0)>>4; //convert to BCD MIN
                  led5=hour & 0x0F;
                  led6=(hour & 0xF0)>>4; //convert to BCD HOUR
                  led7=date & 0x0F;
                  led8=(date & 0xF0)>>4; //convert to BCD DATE
                  led9=month & 0x0F;
                  led10=(month & 0xF0)>>4; //convert to BCD MONTH
                  led11=year & 0x0F;
                  led12=(year & 0xF0)>>4; //convert to BCD YEAR
                  // Sec - Min - Hour
                  PortB=maled7[led1]; RD0=0;//LED1
                  delay_us(200); if(key) RC2=0;
                  RD0=1;
                  PortB=maled7[led2]; RD1=0; //LED2
                  delay_us(200); if(key) RC2=1;
                  RD1=1;
                  PortB=maled7[led3]; RD2=0;//Led3
                  delay_us(200); if(key) RC2=0;
                  RD2=1;
                  PortB=maled7[led4]; RD3=0;//led4
                  delay_us(200); if(key) RC2=1;
                  RD3=1;
                  PortB=maled7[led5]; RD4=0;//led5
                  delay_us(200); RD4=1;
                  PortB=maled7[led6]; RD5=0;//led6
                  delay_us(200); RD5=1;//*/
                  /*/ Date - Month - Year
                  PortB=maled7[led7]; RD0=0;//LED1
                  delay_us(200); RD0=1;
                  PortB=maled7[led8]; RD1=0; //LED2
                  delay_us(200); RD1=1;
                  PortB=maled7[led9]; RD2=0;//Led3
                  delay_us(200); RD2=1;
                  PortB=maled7[led10]; RD3=0;//led4
                  delay_us(200); RD3=1;
                  PortB=maled7[led11]; RD4=0;//led5
                  delay_us(200); RD4=1;
                  PortB=maled7[led12]; RD5=0;//led6
                  delay_us(200); RD5=1; */
                  }

                  void init_time() {
                  int8 date,mth,year,dow,hour,min,sec;
                  I2C_start(); /* The following Enables the Oscillator */
                  I2C_write(0xD0); /* address the part to write */
                  I2C_write(0x00); /* position the address pointer to 0 */
                  I2C_write(0); /* write 0 to the seconds register, clear the CH bit */
                  I2C_write(0x30); /* write slave address + write */
                  I2C_write(0x52); /* hour = 101 0010 12h mode - time =12 */
                  I2C_write(0x07); // Thoi gian ban dau la: 3/11/2005 - 12h/30ph/00sec
                  I2C_write(0x03);
                  I2C_write(0x11);
                  I2C_write(0x05);
                  I2C_stop();

                  I2C_start(); // Write control and status register
                  I2C_write(0xD0); // write slave address + write */
                  I2C_write(0x0e); // write register address, control register */
                  I2C_write(0x20); /* enable osc, bbsqi */
                  I2C_write(0x00);
                  I2C_stop();

                  }

                  void keyscan() {

                  }
                  void main() {
                  int8 temp;
                  int8 i;
                  #bit key = 0x08.7

                  setup_timer_0(RTCC_EXT_L_TO_H|RTCC_DIV_1);
                  set_timer0(0xC4);

                  TrisB=0x00;//output
                  TrisD=0x00;
                  TrisC=0x00;
                  trisc0=1; //Set key input pin
                  TMR0IF=0;
                  RC2=1;
                  if(key) {
                  init_time(); //Thiet lap thoi gian ban dau
                  sec=0;min=59;hour=19;
                  }
                  i2c_start();
                  i2c_write(0xD0);
                  i2c_write(0x00); //set register pointer
                  I2C_start();
                  I2C_write(0xD1); /* write slave address + read */
                  temp = i2c_read(1); /* starts w/last address stored in register pointer */
                  temp = i2c_read(1);
                  temp = i2c_read(1);
                  temp = i2c_read(1);
                  temp = i2c_read(1);
                  temp = i2c_read(1);
                  temp = i2c_read(0);
                  i2c_stop();

                  while(1) {
                  display();
                  }
                  }
                  Ethernet-RS232, PIC Webserver, RFID Reader
                  CallerID, Cảnh báo BTS, ...
                  0988006696
                  linhnc308@gmail.com
                  http://linhnc308.blogspot.com

                  Comment


                  • #10
                    Nguyên văn bởi linhnc308 Xem bài viết
                    Bác Mạnh viết rất hay và cơ bản, ai mới học chắc đêu làm được. Đúng là viết bằng ASM thì hơi khó đọc cho ai mới làm để hiểu tương tận vấn đề, nhưng đừng vì thê mà nản. Tôi thấy ASM thật sự rất hay, no giúp ta hiêu tường tận về phần cứng hơn.
                    - Việc giao tiếp với ngoại vi bên ngoài là một bài toán không thể thiếu khi làm việc với VDK. Rât may là PIC đã hỗ trợ khá nhiều về phần cứng, ta chi việc gọi ra và dùng nó. Về giao tiếp với các IC thời gian thực bằng I2C, trước đây tôi đã làm và dùng bằng CCS. Phai công nhận là viết bẳng CCS công việc dễ dàng hơn nhiều. Vấn đề chính của ta chi còn là việc tôi ưu phần mềm. Tôi định đưa code mà tôi đã viết lên nhưng tiếc là hôm nay trong USB chỉ có bản demo thôi, anh em thông cảm, hôm khăc sẽ pót sau. Trong bản chính có đầy đủ cả phần bàn phím chỉnh và hẹn giờ gồm 4 phím giống như ở đồng hồ Casio mà bạn hay dùng ý (mode - change - alarm set - clear status).
                    Chi tiết về lập trình bạn có thể xem thêm trong datasheet của con DS1337 đề hiểu quá trình gửi lệnh.
                    Chuc thành công!
                    here is demo:

                    #include <16F877A.h>
                    #include <def_877a.h>
                    #FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
                    #use delay(clock=20000000)
                    #use rs232(baud=4800,parity=N,xmit=PIN_C6,rcv=PIN_C7,bi ts=9)
                    #use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3)

                    int8 sec,min,hour,dow,date,month,year;
                    int8 const maled7[10] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90 };
                    int8 so[13]; //sec,min,hour,dow,date,month,year
                    #bit key = 0x06.7
                    void display() {
                    int8 led1,led2,led3,led4,led5,led6,led7,led8,led9,led10 ,led11,led12;
                    int8 temp1,temp2;
                    i2c_start();
                    i2c_write(0xD0);
                    i2c_write(0x00); //set register pointer
                    I2C_start();
                    I2C_write(0xD1); /* write slave address + read */
                    sec = i2c_read(1); /* starts w/last address stored in register pointer */
                    min = i2c_read(1);
                    hour = i2c_read(1) & 0x3F;
                    dow = i2c_read(1);
                    date = i2c_read(1);
                    month = i2c_read(1) & 0x7F;
                    year = i2c_read(0);
                    i2c_stop();
                    led1=sec & 0x0F;
                    led2=(sec & 0xF0)>>4; //convert to BCD SEC
                    led3=min & 0x0F;
                    led4=(min & 0xF0)>>4; //convert to BCD MIN
                    led5=hour & 0x0F;
                    led6=(hour & 0xF0)>>4; //convert to BCD HOUR
                    led7=date & 0x0F;
                    led8=(date & 0xF0)>>4; //convert to BCD DATE
                    led9=month & 0x0F;
                    led10=(month & 0xF0)>>4; //convert to BCD MONTH
                    led11=year & 0x0F;
                    led12=(year & 0xF0)>>4; //convert to BCD YEAR
                    // Sec - Min - Hour
                    PortB=maled7[led1]; RD0=0;//LED1
                    delay_us(200); if(key) RC2=0;
                    RD0=1;
                    PortB=maled7[led2]; RD1=0; //LED2
                    delay_us(200); if(key) RC2=1;
                    RD1=1;
                    PortB=maled7[led3]; RD2=0;//Led3
                    delay_us(200); if(key) RC2=0;
                    RD2=1;
                    PortB=maled7[led4]; RD3=0;//led4
                    delay_us(200); if(key) RC2=1;
                    RD3=1;
                    PortB=maled7[led5]; RD4=0;//led5
                    delay_us(200); RD4=1;
                    PortB=maled7[led6]; RD5=0;//led6
                    delay_us(200); RD5=1;//*/
                    /*/ Date - Month - Year
                    PortB=maled7[led7]; RD0=0;//LED1
                    delay_us(200); RD0=1;
                    PortB=maled7[led8]; RD1=0; //LED2
                    delay_us(200); RD1=1;
                    PortB=maled7[led9]; RD2=0;//Led3
                    delay_us(200); RD2=1;
                    PortB=maled7[led10]; RD3=0;//led4
                    delay_us(200); RD3=1;
                    PortB=maled7[led11]; RD4=0;//led5
                    delay_us(200); RD4=1;
                    PortB=maled7[led12]; RD5=0;//led6
                    delay_us(200); RD5=1; */
                    }

                    void init_time() {
                    int8 date,mth,year,dow,hour,min,sec;
                    I2C_start(); /* The following Enables the Oscillator */
                    I2C_write(0xD0); /* address the part to write */
                    I2C_write(0x00); /* position the address pointer to 0 */
                    I2C_write(0); /* write 0 to the seconds register, clear the CH bit */
                    I2C_write(0x30); /* write slave address + write */
                    I2C_write(0x52); /* hour = 101 0010 12h mode - time =12 */
                    I2C_write(0x07); // Thoi gian ban dau la: 3/11/2005 - 12h/30ph/00sec
                    I2C_write(0x03);
                    I2C_write(0x11);
                    I2C_write(0x05);
                    I2C_stop();

                    I2C_start(); // Write control and status register
                    I2C_write(0xD0); // write slave address + write */
                    I2C_write(0x0e); // write register address, control register */
                    I2C_write(0x20); /* enable osc, bbsqi */
                    I2C_write(0x00);
                    I2C_stop();

                    }

                    void keyscan() {

                    }
                    void main() {
                    int8 temp;
                    int8 i;
                    #bit key = 0x08.7

                    setup_timer_0(RTCC_EXT_L_TO_H|RTCC_DIV_1);
                    set_timer0(0xC4);

                    TrisB=0x00;//output
                    TrisD=0x00;
                    TrisC=0x00;
                    trisc0=1; //Set key input pin
                    TMR0IF=0;
                    RC2=1;
                    if(key) {
                    init_time(); //Thiet lap thoi gian ban dau
                    sec=0;min=59;hour=19;
                    }
                    i2c_start();
                    i2c_write(0xD0);
                    i2c_write(0x00); //set register pointer
                    I2C_start();
                    I2C_write(0xD1); /* write slave address + read */
                    temp = i2c_read(1); /* starts w/last address stored in register pointer */
                    temp = i2c_read(1);
                    temp = i2c_read(1);
                    temp = i2c_read(1);
                    temp = i2c_read(1);
                    temp = i2c_read(1);
                    temp = i2c_read(0);
                    i2c_stop();

                    while(1) {
                    display();
                    }
                    }
                    Rất cảm ơn! nhưng tôi thích cái bên trong của những dòng:

                    I2C_write(0x05);
                    I2C_stop();
                    I2C_start();
                    temp = i2c_read(1);

                    Comment


                    • #11
                      Nguyên văn bởi linhleduong Xem bài viết
                      Rất cảm ơn! nhưng tôi thích cái bên trong của những dòng:

                      I2C_write(0x05);
                      I2C_stop();
                      I2C_start();
                      temp = i2c_read(1);
                      Vậy bạn vào đọc thêm ở đây nhé:
                      https://www2.hcmut.edu.vn/~nqnam/Commu.php

                      I2C_start() chỉ set bit SEN của SSPSTAT, còn I2C_stop() chỉ set bit PEN của SSPSTAT, không có gì là phức tạp cả.

                      Thân,
                      Biển học mênh mông, sức người có hạn

                      Comment


                      • #12
                        Kết nối PIC với EEPROM, DS1307 dùng chuẩn I2C ( hardware)

                        Tôi đã copy y sì đoạn code của ban linh308 vào CCS, nó báo thiếu lỗi, thiếu file như sau:
                        " File can not be open
                        Not in C:\PICC\PICC\devices\def_877a.h
                        Not in C:\PICC\PICC\drivers\def_877a.h
                        .................................................. ....."
                        Mong bạn đưa 2 file đó lên luôn đi cho bà con học tập

                        Comment


                        • #13
                          Nguyên văn bởi nbqvdp Xem bài viết
                          Tôi đã copy y sì đoạn code của ban linh308 vào CCS, nó báo thiếu lỗi, thiếu file như sau:
                          " File can not be open
                          Not in C:\PICC\PICC\devices\def_877a.h
                          Not in C:\PICC\PICC\drivers\def_877a.h
                          .................................................. ....."
                          Mong bạn đưa 2 file đó lên luôn đi cho bà con học tập
                          file def_877a hay user_deff.... hoặc có thể là các file do người lập trình "định nghĩa lại " các giá trị , thanh ghi ...

                          Bạn xem trong phần devices đó có file dạng ( xxx 877 xxx . h ) thì include hay ( use trong CCS) nó vào .
                          Module RF chuyên dụng điều khiển, truyền dữ liệu, thiết kế đề tài, dự án điện tử - chuyển giao công nghệ... ĐT: 0904964977 - email: dientuqueduong@yahoo.com

                          Comment


                          • #14
                            Nguyên văn bởi namqn Xem bài viết
                            Vậy bạn vào đọc thêm ở đây nhé:
                            https://www2.hcmut.edu.vn/~nqnam/Commu.php

                            I2C_start() chỉ set bit SEN của SSPSTAT, còn I2C_stop() chỉ set bit PEN của SSPSTAT, không có gì là phức tạp cả.

                            Thân,
                            Nhiều người bắt đầu tiếp xúc VDK với 8051 do vậy mong bác gửi cho code dùng cho 8051 với! Cảm ơn nhiều!

                            Comment


                            • #15
                              Nguyên văn bởi linhleduong Xem bài viết
                              Nhiều người bắt đầu tiếp xúc VDK với 8051 do vậy mong bác gửi cho code dùng cho 8051 với! Cảm ơn nhiều!
                              Đây là box và luồng dành cho PIC mà bạn. Với lại bao nhiêu code cho dòng 8051 tôi để lại ở VN hết rồi (bây giờ đang ở Anh).

                              Thân,
                              Biển học mênh mông, sức người có hạn

                              Comment

                              Về tác giả

                              Collapse

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

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

                              Collapse

                              Đang tải...
                              X