1. Tuyển Mod quản lý diễn đàn. Các thành viên xem chi tiết tại đây

Mở rộng dung lượng nhớ đến 16MB cho 8051

Chủ đề trong 'Điện - Điện tử - Viễn thông' bởi lamvn, 08/11/2003.

  1. 1 người đang xem box này (Thành viên: 0, Khách: 1)
  1. lamvn

    lamvn Thành viên mới

    Tham gia ngày:
    25/03/2003
    Bài viết:
    266
    Đã được thích:
    1
    Mở rộng dung lượng nhớ đến 16MB cho 8051

    Mình vừa tìm được tài liệu hướng dẫn mở rộng dung lượng nhớ cho 8051 nhưng không có khả năng dịch tài liệu này ra tiếng việt. vậy xin post nguyên văn lên đây cho các bạn tham khảo, bạn nào có khả năng thì dịch ra cho anh em cùng đọc đọc càng hay.

    HOW TO IMPLEMENT
    MORE THAN 64k IN 8051




    It is totaly possible to implement as many code memory you want in the 8051, using some available port pins, or implementing a 74HCT573 8 bits latch (for a max of 16MB) or a 74HCT75 4 bits latch (for a max of 1MB eprom).

    Several devices in the market use that technique, including the famous VGA or SVGA pc card. It has only 64k or 96k of window memory into the PC addressing, but you can have as many memory you want, 1M, 2M even 4MB, since it is pageable into that 64 or 96k "window" from B800h to AFFFh.

    It works like a book. You do not see all pages at the same time, but you can read all the book, page by page, and understand it all.

    As a doctor in a hospital, with many rooms and patients, he can visit one by one, and take care of everyone without any problem, since he knows where he is. At any time the doctor can be called by an emergency, leaving that patient for a later visit, he just need to keep track of what and where he''s doing.

    The 8051 family can access only 64k of code memory and 64k or ram memory, since it uses only 16 addressing bits. But it is actually accessing 128k, 64k for code, and 64k for data. This is done by using different lines (PSEN or /RD or /WR) that mean to which chip the 16 bit address is for.

    A bigger chip, as 8088 or 80486 can access more memory because it has the 17th, 18th and more addressing bits, so it can select other chips or other memory banks inside the same chip.

    What we will do in here is just create those 17th, 18th and more addressing bits. This is quite easy, just connect some other port pins to those addressing bits of your bigger eprom and that''s it. Whenever that particular port pin goes high or low, it will telling the eprom which internal bank is to be used, that''s easy.

    The problem is how to control it in an easy fashion.

    Some solutions in the market, including a Philips forum file, refers to split the 64k in two 32k pages, so the lower 32k still to be always the same, the first 32k of the 512k eprom, as example. It means that whenever you access code memory from 00000h to 07FFFh, it will access the first 32k of that eprom. The other 15 pages of 32k, from eprom address 08001h to 7FFFFh, will be "windowed" in the top 32k, possible to be addressed by the 8051, from 08001h to 0FFFFh.

    Suppose you use 3 port pins P1.0, P1.1 and P1.2 to generate the extra 17th, 18th and 19th addressing lines for the 512k eprom. Whenever you need to access the lower 32k block, those 3 lines need to be down, at low level. Whenever you need to access some other block, those three lines need to be logically set.

    There is a problem, when you reset the 8051, all port pins go to high level, so it will select the higher 32k eprom block, from 78000h to 7FFFFh. It means that your bytes at 78000h will really "boot" your machine, not the code at 00000h. You can bypass that situation and install inverters like 74HCT04 between those port pins and the eprom addressing lines, so at the power on it will address the real first low 32k block.

    This solution implies that the lower 32k will holds all the important tasks as interrupts and other main functions, including the page control to access the other pages and things.

    The solution I am suggesting here uses a different approach. It uses pages of full 64k. It is easier to control, easier to keep track and easier to implement.

    The same problem resides for the power on, when the port pins goes high after the reset. The solution for that is just use a simple instruction as MOV P1,#00h as the first bytes of every 64k page, so whenever the machine is initialized, independently of what is in those three addressing lines P1.0, P1.1 and P1.2, the first instruction just drop the selection for the first 64k page, the correct boot page.

    The idea of use a full 64k page allows you to develop a lot of code in the first page, all main routines and interrupts, including lots of tables and stuff.

    Now, as you repeated in every page the instruction MOV P1,#00h, you need also to repeat in every page a "traffic table". It will take control of where your software is and where to go.

    Differently from what you can be thinking this is quite simple.

    Each page will be created by your e***or assembler as a independent code program. Some of those e***or assembler doesn''t allow more than 64k of code at all.

    Suppose you have 16 long math routines at the second 64k page. The first starts at that page address 0500h, the second at 0700h... in real at the eprom it would be 10500h, and 10700h, now, you just create a jump table starting at 100h of that page. At the 100h: JMP 0500h, and at 0103h: JMP 0700h, so, whenever you need to execute the routine at 500h, just jump to 0100h and it goes there.
    There is a problem with this technique, it nees to be at that page, selected by P1.0, P1.1, P1.2 port bits.

    To make sure that it would be at that page whenever you jump to 100h, you need to implement a little bit that traffic table, and include the selection of those memory banks by those port pins.

    Now, suppose P1.0 is connected to eprom address A16, P1.1 to A17 and P1.2 to A18.

    Then, if the routine pointer by 100h is in the second 64k bank, it means that P1.0 must be at level "1" while P1.1 and P1.2 at level "0", so it is just a matter to do it BEFORE the JMP 0500h instruction.


    TRAFFIC TABLE

    example:


    ORG 100h ; General Return to Caller
    GENRET: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes - Selects the basic lower 64k page, bits P1.0,1,2 = "0"
    Mov P1,A ; 2 bytes - Here selecting the lower 64k page
    Ret ; 1 byte - here already running in that first 64k page.

    ROUT1: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes
    Orl A,#1 ; 2 bytes
    Mov P1,A ; 2 bytes
    Call 0500h ; 3 bytes
    Jmp 0100h ; 2 bytes

    ROUT2: Mov A,P1 ; 2 bytes - Get actual P1 value
    Anl A,#0F8h ; 2 bytes - Keep the other P1 bits intact
    Orl A,#1 ; 2 bytes - Here selects the 64k page, 0 = 0, 1=1, 2=2, etc.
    Mov P1,A ; 2 bytes - Here physically that page is selected
    Call 0700h ; 3 bytes - Here is running in that page, not any more in first 64k
    Jmp 0100h ; 2 bytes - here will reselect first 64k page and return to caller.

    ROUT3: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes
    Orl A,#1 ; 2 bytes
    Mov P1,A ; 2 bytes
    Call 0900h ; 3 bytes
    Jmp 0100h ; 2 bytes


    So, you will need to create this traffic table for all routines you need to call from the basic page to any other page. Calls, Returns, Jumps in the same page doesn''t need to go via this traffic table, since it is already running in that page.

    Then suppose your machine is running at the lower 64k page and you need to call the routine ROUT2, so the CALL ROUT2 will save PC in the stack and jump to ROUT2 that will be located at
    address 0114h, so exactly after the instruction MOV P1,A, the lines at P1.0,1 and 2 will be changing and selecting the second 64k page (since P1.0 now is "1"), and then the next instruction will be the CALL 0700h in the second page already. When that routine finishes, it will returns to the instruction right after the CALL that is the JMP 0100h, in the second page yet. At the 0100h, the GENRET routine just drop all those selection bits, so right after the instruction MOV P1,A, it will be running at the lower page (page zero), and the following RET instruction will return to the ROUT2 routine caller.

    It is that easy.

    You can implement this in several other ways, but the more safe way is just do duplicate the traffic table in every 64k page, exactly the same, starting at the address 0100h.

    Remember that when you are running your e***or assembler, it can not deal with two different routines at the same address, it means, you can not make it understand that you have some routine starting at address 0500h in page 1 and another routine starting at 04F0h in the page zero, both crossing each other. You need to create it as different programs, and then create the same traffic table by hand in each one.

    The GENRET routine, existent in all pages, will reselect to page zero and return to caller.





    INTERRUPTS






    The interrupts follows the same fashion. Whenever an interrupts happens, its routine will jump to one special routine at the traffic table.

    It means that if your machine is running the ROUT2 at 0700h at page 1 and an interrupts happens, you have two choices, duplicate its routine in every page, so you dont need to worry about, or then jump to a special routine in the traffic table, that first save the contents of P1, so it can return to page 1 after the interrupt routine ends, and then jump to a regular traffic routine. The only difference is that when returning from the interrupt routine, it needs to first restore the old P1 saved at the interrupt routine first bytes, different from GENRET routine that always returns to caller in page zero. Example:


    This is duplicated at every page:


    ORG 0023h ; SERIAL PORT INTERRUPT
    Mov A,P1 ;
    PUSH ACC ;
    Call ROUTS ; ROUTS is the traffic routine to jump to page zero serial routine.
    Pop ACC ; Get back P1 old information
    Mov P1,A ; Select old page when interrupts happened
    RETI ; Return from interrupt to the old routine interrupted.

    Personally I preffer to chose the option to have the complete interrupt routines duplicated in every page, for several reasons:

    1) An interrupt routine needs to be small, so it will not create any space problem to duplicate it in every page.

    2) It needs to be speedy, so extra code to control page selection will be an issue

    3) It will be simple to understand.




    USING AN EXTERNAL 74HCT75

    If you do not have those three pins (P1.0-P1.2) available to use as eprom page addressing, you will need to use an external register to do the job, so you can use 74HCT373, 573 or even HCT75 to hold those 3 bits (for 512k eprom), so it must be selected as a regular I/O when you need to change the instructions at the traffic table from the easy MOV P1,A to something different that you need to implement, but it works the same way.




    FINAL CONSIDERATIONS




    I already implemented a voice module that used 1MB eproms and used this technique, it is obvious that you can not run a routine in page zero to read data from another page. The type of instruction MOVC A,@A+DPTR runs easy in the same page, so in my project, I needed to implement the audio routine (Read and Move to DAC) in every page, but it was less than 50 bytes...

    Remember that when switching pages, all you have inside the 8051 still the same, so, you can use the internal ram memory as argument storage between pages to transport values, flags, and so on.

    Another tip;

    Remember that instead to use only DPTR as a 16 bit pointer, you can also use P2 and R0 to address external memory, example;

    Mov DPTR,#0519h
    Clr A
    Movc A,@A+DPTR

    you can use

    Mov P2,#05h
    Mov R0,#19h
    Movc A,@R0

    R0 will select address lower bits to 19h while P2 will be set to select higher address to 05h, so they really suck data from eprom address 0519h into A register.

    Now, remember that you have R1 that can do the same (Movc A,@R1) and also remember that you have 4 sets of R0 and R1, so you can have in real 9 x 16 bits pointers instead of blame the 8051 to have only 1 dptr.

    You can switch between banks with just a single instruction to select register bank0, bank1, bank2 or bank3, according to what you setb or clr bits RS1 and RS0.
  2. txnghia

    txnghia Thành viên mới

    Tham gia ngày:
    13/10/2003
    Bài viết:
    216
    Đã được thích:
    0

    Ðã đọc sơ qua và hiểu ý bài này nói gì và cũng muốn dịch ra cho mọi người đọc. Nhưng để dễ hiểu mình đưa ra vấn đề trước và sau đó sẽ dịch, tuy nhiên cũng sẽ thêm chút mắm muối. Vấn đề ở đây là mở rộng phần giao với bộ nhớ bên ngoài vi điều khiển 8051. Vi điều khiển 8051 ngoài các chức năng thường có như giao diện với ngoại vi qua cổng nối tiếp (UART, I2C, SPI..), cổng I/O, phát tín hiệu điều xung PWM, cung cấp các ngõ ra vô tuyến tính...nó còn có khả năng giao diện với các chip bộ nhớ bên ngoài. Các chip bộ nhớ này thường được dùng để chứa các dữ kiện mà trong lúc hoạt động mà người dùng muốn giữ lại hay lấy các dữ kiện ra để xử lý. Hay ta có thể coi nó như là một kho cất giữ thông tinh, có thể thêm vào hay lấy ra. Bộ nhớ này có thể là Flash, EEPROM (giữ thông tin dù có mất điện nguồn) hay các loại RAM (thông tin biến mất khi mất điện).
    Không phải loại chip nhớ nào cũng có thể giao diện với 8051 này, nếu được cũng qua nhiều bước rất vất vả. Nhưng với loại RAM thường thì thật đơn giản. Ví dụ, các tín hiệu cho 64KByte RAM gồm 16 dây bus địa chị (16-bit địa chỉ), 8 dây bus dữ kiện (8-bít), và 3 hoặc 4 dây bus điều khiển (WR (write) - tín hiệu báo khi bộ xử lý muốn đọc hay viết ra vào RAM, CS (chip select) - khi tín hiệu này bật lên, RAM mới hoạt động, OE (output enable) - bật tín hiệu này cho phép dữ kiện trong RAM ra nằm chờ ở bus dữ kiện và bộ xử lý sau đó sẽ đọc vào, strobe - tại cạnh lên hoặc xuống của tín hiệu này dữ kiện ở bus dữ kiện được ghi vào RAM hay đọc ra khỏi RAM.
    Mọi thứ đều xuông xẻ nếu bộ nhớ bên ngoài cũng có 16 dây địa chỉ (hoặc ít hơn) và 8 bít dữ kiện, cho phép bộ nhớ bằng hoặc dưới 64K byte. Vấn đề ở chỗ là người dùng cần bộ nhớ cao hơn. Thay vì dùng loại vi xử lý (vi điều khiển) khác có nhiều dây bus địa chỉ, người thiết kế muốn vẫn dùng 8051 mà vẫn làm được chuyện đó. Vậy phải làm sao?
    Trong bài đã đưa ra 2 phương cách: Một là dùng một số chân dư của 8051, như các cổng I/O, để làm tăng số dây địa chỉ. Hai là, khi không có các chân dư thi dùng thanh ghi (register) mở thêm chân địa chỉ. Với cách thứ 2, địa chỉ được gởi làm 2 lần, lần thứ nhất gởi các bít cao tới thanh ghi, và được thanh ghi dữ lại đó, sau đó mới gởi ra 16 bít địa chỉ chính. Ðịa chỉ giữ trên thanh ghi và 16 bít gồm thành số bít địa chỉ yên cầu. Thật ra hai các trên là biện pháp chia bộ nhớ ra nhiều phần nhỏ (mỗi phần 64 KByte), khi truy cập dữ kiện trong cùng một phần thi nhanh nhưng khi muốn qua một phần khác, bộ xử lý phải ngưng một tí để đổi tín ra ỏ các chân được dùng làm tăng thêm bít địa chỉ (cách thứ nhất) hay gởi địa chỉ làm 2 lần (cách thứ 2).
    ok, bắt đầu dịch nha.
    Mở rộng dung lượng nhớ đến 16MB cho 8051
    Một điều có thể hoàn toàn thực hiện được để nâng cao khả năng giao diện của 8051 với các chip nhớ có độ chứa cao bằng cách dùng các chân dư sẵn có hay dùng cách thanh ghi chốt 8-bít (có thể là 74HC573) cho bộ nhớ tối đa 16Mbyte hoặc 4 bit (74HCT754) cho chip nhớ tối đa 1Mbyte. Loại thanh ghi này hơi khác loại bình thường một tí. Trong khi với loại thường thì dữ kiện được ghi vào thanh ghi tại cạnh lên hoặc xuống của chân xung clock, thì loại chốt lại cho dữ kiện đi vào thanh ghi trong suốt thời gian chân tín hiệu G (gate) ở mức thấp (hoặc cao tùy loại). Khi tín hiệu chân G nhảy lên cao, dữ kiện được khóa lại trong thanh ghi (vì thế mới có tên là latch hay chốt khóa). Ở loại chốt không có chân CLK mà chỉ có chân G, ngược lại loại kia không có chân G mà chỉ có CLK. Lý do dùng loại chốt là vì dễ ghi vào thanh ghi trong trường hợp này.
    Kỹ thuật này đã đang được dùng cho nhiều thiết bị trong thị trường, gồm các thẻ VGA, SVGA... Các thẻ này trước đây dùng mạng ISA cũ, cũng 16 bit địa chỉ. Ðể các thẻ này có thể giữ được dữ kiện lên đến nhiều Mbyte, thì đia chỉ được gởi làm 2 lần từ bộ xử lý đến thẻ qua mạng ISA. Thẻ nhận cái thứ nhất và chứa vào một chỗ (như thanh ghi nói ở trên), chờ đến cái thứ 2 được nhận thì thẻ mới quyết định đưa vào hay lấy ra dữ kiện ở địa chỉ nào của bộ nhớ.
    Nó làm việc như một quyển sách. Bạn không cần thấy tất cả các trang cùng một lú, nhưng bạn có thể đọc cả quển, từng trang một, và hiểu hết được nó.
    Giống như một bác sĩ ở bệnh viện, với nhiều phòng và bệnh nhân. Khi ông biết ông đang ở đâu trong bệnh viện, ông ta có thể thăm bệnh từng người từng một và chăm sóc cho tất cả mà không vấn đề gì (người viết bản gốc đang dùng phương pháp tương tự hóa đấy). Khi đang săn sóc bệnh nhân ở phòng này và khi được gọi cứu cấp, ông có thể để tạm rời bệnh nhân phòng đó và đi đến ngay chỗ cần cứu cấp. Sau khi lo xong thì lại trở về bệnh nhân cũ. (đoạn này giúp hình dung ra khi chuyển địa chỉ từ trang này sang trang kia trong bộ nhớ).
    ...ahhhh...đi ngủ đây...mai dịch tiếp vậy...
  3. minhgiang84

    minhgiang84 Thành viên mới

    Tham gia ngày:
    23/08/2003
    Bài viết:
    14
    Đã được thích:
    0
    Hoan hô anh txnghia ! Anh thật nhiệt tình và đáng yêu, nếu là con gái em sẽ nói với anh rằng anh rất dễ thương. bởi vì điều thường thấy ở những người dể thương là biết ham thích làm hài lòng người khác bằng hy sinh phục vụ.
  4. txnghia

    txnghia Thành viên mới

    Tham gia ngày:
    13/10/2003
    Bài viết:
    216
    Đã được thích:
    0

    Cảm ơn minhgiang84! khuyến khích của minhgiang như một ly cà fê buổi tuối, giúp mình thoải mái, hết buồn ngủ, ngồi trước máy thêm một vài giờ nữa dù đã khuya lắm rồi. Mà sao minhgiang không phải là con gái nhỉ
  5. txnghia

    txnghia Thành viên mới

    Tham gia ngày:
    13/10/2003
    Bài viết:
    216
    Đã được thích:
    0
    Bài này có vài đoạn viết hơi khó hiểu, tớ phải ráng hiểu và viết lại theo ý mình. Còn lại thì cố dịch theo ý của người viết. Với cách viết tự do, tác giả đã đưa ra giải pháp chi tiết cho cả phần cứng và thuật lập mã trình độc đáo, làm tăng độ linh hoạt của 8051.

    Phần dịch tiếp theo
    Nếu chỉ có 16 dây địa chỉ, họ 8051 chỉ có thể truy cập 64KB của bộ nhớ chứa mã chương trình hoặc 64KB của bộ nhớ RAM mà thôi. Nhưng thật ra nó truy cập đến được cả hai, tổng lượng chứa lên đến 128KB. Tuy là chỉ dùng 16 bít địa chỉ nhưng với cách dùng các tín hiệu điều khiển riêng biệt (PSEN - Program Store Enable, /RD - Read, hoặc /WR -Write) đã giúp giải quyết được vấn đề. Ví dụ khi muốn đọc dữ kiện ra khỏi bộ nhớ chứa mã trình thì chỉ có tín hiệu, đặt tên là /RD1, nối vào bộ nhớ này được bật lên, dữ kiện mã trình được gởi ra bus dữ kiện, và được bộ xử lý đọc. Trong khi đó dù bộ nhớ RAM cùng dùng chung 16 dây địa chỉ và 8 dây dữ kiện với bộ nhớ chứa mã trình nhưng nó không thực hiện quá trình gởi dữ kiện ra bus dữ kiện vì tín hiệu đọc, đặt tên là /RD2, không được bật lên. Tín hiệu đọc /RD1 và /RD2 là tín hiệu điều khiển riêng biệt nói ở trên.
    Với chip lớn hơn như 8088, 80486 thì nó có thể truy cập bộ nhớ lớn hơn 64KB vì có sẵn dây địa chỉ thứ 17, 18, 19, ...
    Những gì ta sẽ làm ở đây là tạo ra dây địa chỉ thứ 17, 18, 19..cho 8051, số lượng dây địa chỉ tùy chíp nhớ bên ngoài lớn hay nhỏ. Ðiều này khá dễ dàng, dùng các chân I/O còn lại của 8051 và đặt các chân đó như là dây thứ 17, 18.. và chỉ thế thôi. Mỗi khi tín hiệu chân I/O kia ở mức cao hay thấp, sẽ cho biết vùng nào trong bộ nhớ đang được truy cập (mỗi vùng rộng 64KB). Với 8051 thì bộ nhớ 1MB được chia làm 16 vùng, bộ nhớ 16MB được chia làm 256 vùng. Hay ta có thể nghĩ mỗi vùng là một trang trong quyển tập. Quyển 1MB có 16 trang và quyển 16MB có 256 trang. Mỗi lần muốn đọc/viết từ trang này sang trang khác, mất tí thì giờ để lật trang, tức là thời gian thay đổi giá trị ra của chân I/O.
    Vấn đề là làm sao điều khiển các dây I/O và 16 dây địa chỉ hoạt động nhịp nhàng và dễ dàng.
    Một vài phương pháp hiện hành, gồm cả một tài liệu trong diễn đàn của hãng Philips, đưa ra cách phân chia 64K thành 2 trang, mỗi trang là 32K. Ví dụ một EPROM 512K được chia làm 16 trang, mỗi trang 32K. Trang thứ nhất bắt đầu từ địa chỉ 00000h đến 07FFFh và 15 trang còn lại nằm trong khoảng từ 08001h đến 7FFFFh. Mỗi khi bạn truy cập dữ kiện trong khoảng địa chỉ từ 00000h đến 07FFFh nó sẽ truy cập trang 32K thứ nhất của EPROM 512K. Trong khi các chân I/O được tạo ra thêm dẫn bạn đến từng trang thì các dây địa chỉ sẵn có của 8051 truy cập đến từng byte trong một trang.
    Giả sử bạn dùng 3 chân của nhóm cổng P1 (P1.0, P1.1, P1.2) để tạo ra đường dây địa chỉ thứ 17, 18, và 19 để có thể giao diện với EPROM 512K. Khi bạn muốn đọc/viết ở trang thứ nhất, thì mức lô-gích được gởi ra trên 3 dây địa chỉ đó phải là mức "0". Khi cần đọc/viết ở một trang khác, các chân đó phải được gởi ra những tín hiệu thích hợp.
    Có vấn đề! Trong giai đoạn khởi động của 8051, tất cả các chân ở các nhóm cổng (trong đó có chân P1.0, P1.1, và P1.2) được ngầm định ở mức lôgich "1", lúc đó 19 bít địa chỉ (gồm 16 bít có sẵn và 3 bít được tạo thêm) trở thành 70000h (111.0000.0000.0000.0000b). Thay vì bộ xử lý 8051 khởi động với các mã trình ở địa chỉ 00000h (000.0000.0000.0000b), nó lại khởi động tại địa chỉ 70000h, và đương nhiên sẽ gây lỗi. Bạn có thể khắc phục nó bằng cách dùng cổng đảo 74HCT04 đặt giữa chân cổng (P1.0, P1.1, và P1.2) và các dây địa chỉ của EPROM.
    Giải pháp này dùng 32K đầu tiên để giữ tất cả các bước hoạt động quan trọng như ngắt và hàm chính, mã điều khiển truy cập đến trang khác và nhiều chức năng khác.
    Phương pháp tôi đang đề xuất thì hơi khác một chút. Thay vì chia mỗi trang là 32K, tôi dùng nguyên một khung địa chỉ 16 bít (64K), nó dễ điều khiển, xử lý và thực hiện.
    Phương pháp này vẫn có vấn đề lúc bật điện, tín hiệu chân cổng ở mức lô-gích "1". Giải quyết cho vấn đề này là dùng lệnh MOV P1, #00h đặt tại mỗi byte đầu tiên của mỗi trang. Khi vừa bật điện, 8051 sẽ truy cập đến byte đầu tiên của một trang nào đó, sau đó đọc và thi hành lệnh MOV P1, #00h. Nhóm chân cổng P1(P1.0, P1.1, P1.2) sau đó trở về mức "0", đưa địa chỉ về trang thứ nhất nơi chứa các mã khởi động. Mã trình sẽ bắt đầu từ đây.
    Cách chia mỗi trang làm 64K cho phép bạn khai triển nhiều mã trình trong một trang đầu tiên, gồm cả các hàm chính, hàm ngắt, bản giá trị, và nhiều cái khác.
    Bây giờ, vì bạn đặt lệnh MOV P1,#00h ở byte đầu tiên của mỗi trang, bạn cũng cần phải đặt ở mỗi trang một bảng chuyển tiếp, giúp chương trình biết nó đang chạy tới phần nào và phần nào sẽ đi đến.
    Khác với những gì bạn đang nghĩ, điều này khá đơn giản.
    Mỗi trang sẽ được tạo ra từ chương trình nhập mã assembler như là một chương trình độc lập. Một số chương trình nhập mã assembler không cho phép lớn hơn 64K
    Giả sử bạn có 16 hàm toán nằm ở trang 64K thứ hai. Hàm thứ nhất bắt đầu tại địa chỉ 0500h, hàm thứ hai bắt đầu tại 0700h, ...của trang thứ hai. Bây giờ bạn cần tạo một bảng chuyển tiếp ở địa chỉ 100h, và từ bảng này chương trình biết địa chỉ nào cần nhảy đến. Tại địa chỉ 100h: JMP 0500h, và tại 0103h: JMP 0700h. Vì vậy mỗi khi cần thực hiện một phép tính mà hàm nó bắt đầu ở địa chỉ 500h thì chương trình cần được lập trình nhảy đến địa chỉ 0100h, và ở đây bảng chuyển tiếp sẽ dẫn chương trình đến hàm ở địa chỉ 500h.
    Có một trục trặc với thuật này. Trước tiên cần phải đưa chương trình đến đúng trang có chứa thông tin ta cần. Các trang này được quyết định bởi tín hiệu ở các chân P1.0, P1.1, P1.2.
    Ðể biết chắc trang cần đến khi nhảy đến 100h, bạn cần thiết lập một bảng giá trị nhỏ chứa thông tin chọn lựa trang với các giá trị của chân cổng P1.0, P1.1, P1.2.
    Bây giờ, giả sử P1.0 được nối đến chân địa chỉ A16 của EPROM, P1.1 nối đến A17, và P1.2 nối đến A18.
    Sau đó, nếu con chỉ nằm trong trang 64K thứ hai, nghĩa là mức lô-gích ở chân P1.0 phải là "1", P1.1 là "0", P1.2 là "0". Vậy đây làm việc cần làm TRƯỚC KHI thực hiện bước nhảy đến 0500h.
    Bảng Chuyển Tiếp
    Ví dụ:
    ORG 100h ; General Return to Caller
    GENRET: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes - Tạo giá trị có 3 bít cuối "0"
    Mov P1,A ; 2 bytes - Gởi giá trị này ra cổng A
    Ret ; 1 byte - và đưa con chỉ đến trang đầu tiên
    ROUT1: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes
    Orl A,#1 ; 2 bytes
    Mov P1,A ; 2 bytes
    Call 0500h ; 3 bytes
    Jmp 0100h ; 2 bytes
    ROUT2: Mov A,P1 ; 2 bytes - Get actual P1 value
    Anl A,#0F8h ; 2 bytes - Keep the other P1 bits intact
    Orl A,#1 ; 2 bytes - Here selects the 64k page, 0 = 0, 1=1, 2=2, etc.
    Mov P1,A ; 2 bytes - Here physically that page is selected
    Call 0700h ; 3 bytes - Here is running in that page, not any more in first 64k
    Jmp 0100h ; 2 bytes - here will reselect first 64k page and return to caller.
    ROUT3: Mov A,P1 ; 2 bytes
    Anl A,#0F8h ; 2 bytes
    Orl A,#1 ; 2 bytes
    Mov P1,A ; 2 bytes
    Call 0900h ; 3 bytes
    Jmp 0100h ; 2 bytes
    Vậy bạn cần tạo bảng chuyển tiếp này cho tất cả các hàm bạn cần gọi từ trang gốc đến một trang khác. Gọi (calls), Trở về (Returns), Nhảy (Jumps) trong cùng một trang thì không cần phải đi qua bảng chuyển tiếp vì con chỉ (pointer) lúc này đang ở trang này rồi.
    Sau đó, giả sử máy của bạn đang chạy ở trang thứ nhất và bạn cần gọi hàm ROUT2. CALL ROUT2 sẽ đưa giá trị của con chỉ vào trong một chỗ chứa tạm (stack) và nhảy đến ROUT2 ở địa chỉ 0114h. Lệnh MOV P1,A gởi giá trị ra chân P1.0, 1, 2 để định trang (P1.0 = "1"). Sau khi con chỉ đã được đưa đến trang thứ 2 thì lệnh thực hiện kế tiếp là CALL 0700h. Khi hàm thực hiện xong, con chỉ lại trở về JMP 0100h (vẫn đang trong trang 2). Tại 0100h, hàm GENRET sẽ đưa tín hiệu P1.0, 1, 2 về "0", con chỉ lại trở về trang thứ nhất. Giá trị con chỉ lúc nảy chứa tạm ở một nơi (stack) bây giờ được đưa trở, và chương trình bắt đầu thực hiện các bước kế tiếp.
    Dễ dàng thế thô.
    Bạn có thể thực hiện theo nhiều cách khác nhau, nhưng cách an toàn là tạo các bảng chuyển tiếp giống hệt nhau ở mỗi trang 64K, bắt đầu tại địa chỉ 0100h.
    Nhớ rằng khi bạn đang chạy chương trình nhập assembler, nó không thể xử lý được 2 hàm khác nhau có cùng địa chỉ, nghĩa là bạn không thể làm cho nó hiểu là bạn có một hàm bắt đầu ở địa chỉ 0500 ở trang thứ 2 và một hàm như thế lại bắt đầu tại 04F0h ở trang thứ nhất, cả hai chéo cẳng ngỗng. Bạn cần tạo chúng như những hàm khác nhau và làm bảng chuyển tiếp cho mỗi hàm.
    Hàm GENRET, có trong tất cả các trang, sẽ đưa con chỉ về trang thứ nhất và trả lại lệnh gọi.
    Ngắt (interrupts)
    Các ngắt cũng được thực hiện theo cùng kiểu. Mỗi có một tín hiệu ngắt xảy ra, con chỉ nhảy đến một hàm đặc biệt tại bảng chuyển tiếp.
    Nghĩa là nếu máy của bạn đang chạy ROUT2 tại địa chỉ 0700 ở trang thứ 2, và một tín hiệu ngắt xảy ra. Bạn có 2 chọn lựa, sao lại hàm của nó ở mỗi trang giúp bạn không cần phải lo lắng về nó, hoặc sau đó nhảy đến một hàm đặc biệt trong bảng chuyển tiếp, và cần trước tiên chứa giá trị P1 để sau khi thực hiện ngắt, con chỉ biết đường trở về trang đang chạy. Chỉ một điểm khác là sau thực hiện hàm ngắt, con chỉ trở trang đang chạy khi có ngắt xảy ra. Với các hàm bình thường thì sau khi thực hiện xong, con chỉ chỉ trở về lại trang thứ nhất mà thôi.
    Ví dụ:
    Phần dưới đây được sao lại cho mỗi trang.
    ORG 0023h ; SERIAL PORT INTERRUPT
    Mov A,P1 ;
    PUSH ACC ;
    Call ROUTS ; ROUTS is the traffic routine to jump to page zero serial routine.
    Pop ACC ; Get back P1 old information
    Mov P1,A ; Select old page when interrupts happened
    RETI ; Return from interrupt to the old routine interrupted.
    Riêng về phần tôi, tôi thích chọn cách có các hàm ngắt hoàn chỉnh được sao lại cho mỗi trang với nhiều lý do:
    1) Hàm ngắt cần phải nhỏ, do đó nó không tạo ra vấn đề chiếm chỗ khi được sao lại trong mỗi trang.
    2) Nó cần phải tốc độ, do đó các mã trình thêm vào để điều khiển chọn trang không làm chậm máy.
    3) Nó sẽ đơn giản dễ hiểu.
    Dùng 74HCT75
    Nếu 8051 không còn lại chân nào (ví dụ P1.0, 1, 2) cho việc giúp truy cập đến từng trang trong bộ nhớ, bạn sẽ cần dùng một thanh ghi bên ngoài để làm việc đó. Bạn có thể chọn 74HCT373, 573, hoặc ngay cả 74HCT75 để giữ 3 bít đó (cho EPROM 512K). Trong bảng chuyển tiếp, bạn cần viết trình cho nó, sao cho các ngõ ra các thanh ghi có mức lô gích như của các chân cổng P1.0, 1, 2.
    Tôi đã thực hiện một phần mạch giọng nói dùng 1MB EPROM và thuật này. Rõ ràng là bạn không thể chạy một hàm ở trang thứ nhất để đọc dữ liện ở một trang khác. Lệnh MOVC A,@ + DPTR chạy dễ hơn trong cùng một trang nên trong dự án nên tôi cần thực hiện một hàm cho âm thanh cho mỗi trang (đọc và chuyển dữ kiện ra DAC - Digital to Analog Converter), mỗi hàm nhỏ hơn 50 byte.
    Nhớ rằng chi chuyển từ trang này đến trang kia, tất cả những gì bên trong 8051 vẫn không đổi, bạn có thể dùng phần nhớ nội như nơi chứa các argument giữa mỗi trang, giá trị chuyển tiếp, cờ, v.v...
    Một cách khác; Nhớ rằng thay vì chỉ dùng DPTR (Data Pointer) như là một con chỉ 16 bít, bạn cũng có thể dùng P2 và R0 để tạo thêm bít địa chỉ cho bộ nhớ ngoại.
    Mov DPTR,#0519h
    Clr A
    Movc A,@A+DPTR
    you can use
    Mov P2,#05h
    Mov R0,#19h
    Movc A,@R0
    Trong khi R0 gán cho 8-bit nhỏ của thanh A giá trị 19h thì P2 gán cho 8-bít lớn giá trị 05h. Lệnh Movc A,@R0 lấy dữ kiện từ địa chỉ 0519h và chứa và thanh ghi A.
    Bây giờ nhớ rằng bạn có R1 có thể làm việc tương tự (Mov A,@R1) và cũng nhớ rằng bạn có 4 bộ R0 và R1, do đó thật sự có cho chỉ 9x16 bít thay vì đổ tội cho 8051 chỉ có 1 DPTR
    Bạn có thể chuyển trang, giữa trang 1, 2, 3 hay 4 chỉ với một lệnh đơn set hoặc clr bít RS1 và RS0.

Chia sẻ trang này