Graphic LCD

( 26 Votes )

Nội dung Các bài cần tham khảo trước
  1. Bạn sẽ đi đến đâu.
  2. Graphic LCD.
  3. AVR và Graphic LCD.
  4. Ví dụ điều khiển Graphic LCD bằng thư viện myGLCD.

Download ví dụ

Download phần mềm G.Edit


 

I. Bạn sẽ đi đến đâu.    

     Trong bài ứng dụng này tôi trình bày về cấu trúc và cách điều khiển Graphic LCD loại dot không màu. Công cụ chính cũng là 2 bộ phần mềm quen thuộc WinAVR, Proteus và phần mềm biên tập Graphic LCD, G.Edit.
       Sau bài này, tôi hy vọng bạn có thể hiểu và thực hiện được:
       - Cấu trúc Graphic LCD 128x64 và chip điều khiển KS0108.
       - Nguyên lý hoạt động Graphic LCD.
       - Phát triển 1 thư viện điều khiển Graphic LCD 128x64 cho AVR.
       - Ví dụ điều khiển Graphic LCD 128x64 bằng AVR.

II. Graphic LCD.

      Graphic LCD (gọi tắt là GLCD) loại chấm không màu là các loại màn hình tinh thể lỏng nhỏ dùng để hiển thị chữ, số hoặc hình ảnh. Khác với Text LCD, GLCD không được chia thành các ô để hiển thị các mã ASCII vì GLCD không có bộ nhớ CGRAM (Character Generation RAM). GLCD 128x64 có 128 cột và 64 hàng tương ứng có 128x64=8192 chấm (dot). Mỗi chấm tương ứng với 1 bit dữ liệu, và như thế cần 8192 bits hay 1024 bytes RAM để chứa dữ liệu hiển thị đầy mỗi 128x64 GLCD. Tùy theo loại chip điều khiển, nguyên lý hoạt động của GLCD có thể khác nhau, trong bài này tôi giới thiệu loại GLCD được điều khiển bởi chip KS0108 của Samsung, có thể nói GLCD với KS0108 là phổ biến nhất trong các loại GLCD loại này (chấm, không màu). Hình 1 là hình ảnh thật của 1 GLCD 128x64 điều khiển bởi KS0108.

Graphic LCD

Hình 1. Graphic LCD 128x64.

      Chip KS0108 chỉ có 512 bytes RAM (4096 bits = 64x64) và vì thế chỉ điều khiển hiển thị được 64 dòng x 64 cột. Để điều khiển GLCD 168x64 cần 2 chip KS0108, và thực thế trong các loại GLCD có 2 chip KS0108, GLCD 128x64 do đó tương tự 2 GLCD 64x64 ghép lại.  Chúng ta sẽ lần lượt khảo sát sơ đồ chân, cấu trúc bộ nhớ và nguyên lý hoạt động của GLCD, chip KS0108 trong phần tiếp theo.

1. Sơ đồ chân GLCD 128x64. 

       Các GLCD 128x64 dùng KS0108 thường có 20 chân trong đó chỉ có 18 chân là thực sự điều khiển trực tiếp GLCD, 2 chân (thường là 2 chân cuối 19 và 20) là 2 chân Anode và Cathode của LED nền. Trong 18 chân còn lại, có 4 chân cung cấp nguồn và 14 chân điều khiển+dữ liệu. Khác với các Text LCD HD44780U, GLCD KS0108 không hỗ trợ chế độ giao tiếp 4 bit, do đó bạn cần dành ra 14 chân để điều khiển 1 GLCD 128x64. Sơ đồ chân phổ biến của GLCD 128x64 được mô tả trong bảng 1. 
Bảng 1. Sơ đồ chân GLCD GDM-12864-04.

So do chan

      Chú ý là trên một số GLCD, thứ tự các chân có thể khác (như GLCD WG12864A2…) nhưng số lượng và chức năng chân thì không đổi. Hình 2 mô tả cách kết nối GLCD với nguồn và mạch điều khiển.

Connect GLCD

Hình 2. Kết nối GLCD. 

     Chân VSS  được nối trực tiếp với GND, chân VDD nối với nguồn +5V, một biến trở khoảng 20K được dùng để chia điện áp giửa Vdd và Vee cho chân Vo, bằng cách thay đổi giá trị biến trở chúng ta có thể điều chỉnh độ tương phản của GLCD. Các chân điều khiển RS, R/W, EN và các đường dữ liệu được nối trực tiếp với vi điều khiển. Riêng chân Reset (RST) có thể nối trực tiếp với nguồn 5V.

      EN (Enable): cho phép một quá trình bắt đầu, bình thường chân EN được giữ ở mức thấp, khi một thực hiện một quá trình nào đó (đọc hoặc ghi GLCD), các chân điều khiển khác sẽ được cài đặt sẵn sàng, sau đó kích chân EN lên mức cao. Khi EN được kéo lên cao, GLCD bắt đầu làm thực hiện quá trình được yêu cầu, chúng ta cần chờ một khoảng thời gian ngắn cho GLCD đọc hoặc gởi dữ liệu. Cuối cùng là kéo EN xuống mức thấp để kết thúc quá trình và cũng để chuẩn bị chân EN cho quá trình sau này.

      RS (Register Select): là chân lựa chọn giữa dữ liệu (Data) và lệnh (Instruction), vì thế mà trong một số tài liệu bạn có thể thấy chân RS được gọi là chân DI (Data/Instruction Select). Chân RS=1 báo rằng tín hiệu trên các đường DATA (D0:7) là dữ liệu ghi hoặc đọc từ RAM của GLCD. Khi RS=0, tín hiệu trên đương DATA là một mã lệnh (Instruction).

      RW (Read/Write Select): chọn lựa giữa việc đọc và ghi. Khi RW=1, chiều truy cập từ GLCD ra ngoài (GLCD->AVR). RW=0 cho phép ghi vào GLCD. Giao tiếp với GLCD chủ yếu là quá trình ghi (AVR ->GLCD), chỉ duy nhất trường hợp đọc dữ liệu từ GLCD là đọc bit BUSY và đọc dữ liệu từ RAM. Đọc bit BUSY thì chúng ta đã khảo sát cho Text LCD, bit này báo GLCD có đang bận hay không, việc đọc này sẽ được dùng để viết hàm wait_GLCD. Đọc dữ liệu từ RAM của GLCD là một khả năng mới mà Text LCD không có, bằng việc đọc ngược từ GLCD vào AVR, chúng ta có thể thực hiện nhiều phép logic hình (hay mặt nạ, mask) làm cho việc hiển thị GLCD thêm thú vị.      

     CS2 và CS1  (Chip Select): như tôi đã trình bày trong phần trên, mỗi chip KS0108 chỉ có khả năng điều khiển một GLCD có kích thước 64x64, trên các GLCD 128x64 có 2 chip KS0108 làm việc cùng nhau, mỗi chip đảm nhiệm một nữa LCD, 2 chân CS2 và CS1 cho phép chọn một chip KS0108 để làm việc. Thông thường nếu CS2=0, CS1=1 thì nửa trái được kích hoạt, ngược lại khi CS2=1, CS1=0 thì nửa phải được chọn. Chúng ta sẽ hiểu rõ hơn cách phối hợp làm việc của 2 nửa GLCD trong phần khảo sát bộ nhớ của LCD.

2. Tổ chức bộ nhớ. 

       Chip KS0108 có một loại bộ nhớ duy nhất đó là RAM, không có bộ nhớ chứa bộ font hay chứa mã font tự tạo như chip HD44780U của Text LCD. Vì vậy, dữ liệu ghi vào RAM sẽ được hiển thị trực tiếp trên GLCD. Mỗi chip KS0108 có 512 bytes RAM tương ứng với 4096 chấm trên một nửa (64x64) LCD. RAM của KS0108 không cho phép truy cập từng bit mà theo từng byte, điều này có nghĩa là mỗi lần chúng ta viết một giá trị vào một byte nào đó trên RAM của GLCD, sẽ có 8 chấm bị tác động, 8 chấm này nằm trên cùng 1 cột. Vì lý do này, 64 dòng GLCD thường được chia thành 8 pages, mỗi page có độ cao 8 bit và rộng 128 cột  (cả 2 chip gộp lại). Hình 3 mô tả “bề mặt” một GLCD và cũng là cách sắp xếp RAM của các chip KS0108.

GLCD RAM

Hình 3. Tổ chức của RAM.  

      Tổ chức RAM của 2 chip KS0108 trái và phải hoàn toàn tương tự, việc đọc hay ghi vào RAM của 2 chip cũng được thực hiện như nhau. Chúng ta sẽ chọn nửa trái GLCD để khảo sát. Như bạn thấy trên hình 3, 64 dòng từ trên xuống dưới được chia thành 8 “dãy” mà ta gọi là 8 pages. Page trên cùng là page 0 và page  dưới cùng la page 7. Trong các GLCD, page còn được gọi là địa chỉ X (X address), hay nói cách khác X=0 là địa chỉ của page trên cùng, tương tự như thế, X=7 là địa chỉ của page dưới cùng. Mỗi page chứa 64 cột (chỉ xét 1 chip KS0108), mỗi cột là một byte RAM 8 bit, mỗi bit tương ứng với 1 chấm trên LCD, bit có trọng số thấp (LBS - tức bit D0 như trong hình 3) tương ứng với chấm trên cao nhất. Bit có trọng số cao nhất (MBS - tức bit D7 như trong hình 3) tương ứng với chấm thấp nhất trong 1 page. Thứ tự các cột trong 1 page gọi là địa chỉ Y (Y address), như thế cột đầu tiên có địa chỉ Y = 0 trong khi cột cuối cùng có địa chỉ Y là 63. Bằng cách phối hợp địa chỉ X và địa chỉ Y chúng ta xác định được vị trí của byte cần đọc hoặc ghi. Chip KS0108, tất nhiên, sẽ hỗ trợ các lệnh di chuyển đến địa chỉ X và Y để ghi hay đọc RAM. Hãy quan sát hình 4 để xem cách mà một chữ cái ‘a’ được hiển thị trên GLCD.

'a' on GLCD

Hình 4. Hiển thị chữ cái ‘a’ trên GLCD.  

     Trong cách hiển thị ở hình 4, chữ ‘a’ chỉ nằm trong page 0, tức X=0. Muốn hiển thị chữ cái ‘a’ chúng ta cần ghi vào các cột (địa chỉ Y) của page 0 lần lượt các giá trị như sau: 0, 228, 146, 74, 252 và 128…., xem bảng bên dưới.

'a' on GLCD

 3. Tập lệnh cho chip KS0108. 

       Bảng 2 tóm tắt các lệnh của chip KS0108.

Instruction set

      So với HD44780U của Text LCD, lệnh cho KS0108 của GLCD đơn giản và ít hơn và vì thế viết chương trình điều khiển GLCD cũng tương đối dễ hơn Text LCD. Có tất cả 7 lệnh (Instruction) có thể giao tiếp với KS0108. Tôi sẽ lần lượt giải thích ý nghĩa và cách sử dụng của từng lệnh.

      - Display ON/OFF – Hiển thị GLCD: lệnh này cho phép GLCD hiển thị nội dung trên RAM ra “bề mặt” GLCD. Để viết lệnh này cho GLCD, 2 chân RS và RW cần được kéo xuống mức thấp (RS=0: đây là Instrucion, RW=0: AVR->GLCD). Mã lệnh (code) được chứa trong 7 bit cao (D7:1) và bit D0 chứa thông số. Quan sát bảng 2, dễ thấy mã lệnh nhị phân cho Display ON/OFF là 0011111x (0x3E+x) trong đó x=1: cho phép GLCD hiển thị, x=0: tắt hiển thị.

       - Set Address – chọn địa chỉ: đúng hơn đây là lệnh chọn cột hay chọn địa chỉ Y. Hai bit D7 và D6 chứa mã lệnh (01000000=0x40=64) và 6 bit còn lại chứa chỉ số của cột muốn di chuyển đến. Chú ý là mỗi nửa GLCD có 64 cột nên cần 6 bit để chứa chỉ số này (26=64). Vậy lệnh này có dạng 0x40+Y. Ví dụ nếu chúng ta muốn di chuyển đến cột 36 chúng ta ghi vào GLCD mã lệnh: 0x40+36. Hai chân RS và RW được giữ ở mức thấp khi thực hiện lệnh này.

      - Set Page – chọn trang: lệnh cho phép chọn page (hay địa chỉ X) cần di chuyển đến, do GLCD chỉ có 8 pages nên chỉ cần 3 bit để chứa địa chỉ page. Mã lệnh cho lệnh này có dạng 0xB8+X. Trong đó biến X là chỉ số page cần di chuyển đến. Hai chân RS và RW được giữ ở mức thấp khi thực hiện lệnh này.

     - Display Start Line – chọn line đầu tiên: hay còn gọi là lệnh “cuộn”, lệnh này cho phép di chuyển toàn bộ hình ảnh trên GLCD (hay RAM) lên phía trên một số dòng nào đó, chúng ta gọi là LOffset. Số lượng LOffset có thể từ 0 đến 63 nên cần 6 bit chứa giá trị này. Mã lệnh Display Start Line có dạng 0xC0+LOffset. Hai chân RS và RW được giữ ở mức thấp khi thực hiện lệnh này. Khi di chuyển GLCD lên phía trên, phần dữ liệu phía trên bị che khuất sẽ  “cuộn” xuống phía dưới. Hình 5 là một ví dụ “cuộn” GLCD lên 20 dòng. 

Connect LCD

     - Status Read – đọc trạng thái GLCD: đây là một trong 2 lệnh đọc từ GLCD. Cũng giống như với Text LCD, lệnh đọc trạng thái GLCD chủ yếu để xét bit BUSY (bit  thứ 7) xem GLCD có đang bận hay không, lệnh này sẽ được dùng để viết một hàm wait_GLCD chờ cho đến khi GLCD rảnh. Vì đây là lệnh đọc từ GLCD nên chân RW phải được set lên mức 1 trước khi thực hiện, chân RS vẫn ở mức thấp (đọc Instruction).

      - Write Display Data – ghi dữ liệu cần hiển thị vào GLCD hay RAM: vì đây là 1 lệnh ghi dữ liệu hiển thị nên chân RS cần được set lên 1 trước khi thực hiện, chân RW giữ ở mức 0. Lệnh này cho phép ghi một byte dữ liệu vào RAM của KS0108 và cũng là dữ liệu sẽ hiển thị lên GLCD tại vị trí hiện hành của 2 con trỏ địa chỉ X và Y. 8 bit dữ liệu này sẽ tương ứng với 8 chấm trên cột Y ở page X. Chú ý là sau lệnh Write Display Data, địa chỉ cột Y tự động được tăng lên 1 và vì thế nếu có một dữ liệu mới được ghi, dữ liệu mới sẽ không “đè” lên dữ liệu cũ. Việc tăng tự động địa chỉ Y rất có lợi cho việc ghi dữ liệu liên tiếp, nó giúp giảm thời gian set lại địa chỉ cột Y. Sau khi thực hiện ghi ở cột Y=63 (cột cuối cùng trong 1 page, đối với 1 chip KS0108), Ysẽ về 0.

     - Read Display Data – đọc dữ liệu hiển thị từ GLCD (cũng là dữ liệu từ RAM của KS0108): lệnh đọc này mới so với Text LCD, nó cho phép chúng ta đọc ngược 1 byte dữ liệu từ RAM của KS0108 tại vị trí hiện hành về AVR. Sau khi đã đọc được giá trị tại vị trí hiện hành, chúng ta có thể thực hiện các phép Logic như đảo bit, or hay and…làm tăng khả năng thao tác hình ảnh. Trước khi thực hiện đọc chúng ta cần di chuyển đến vị trí muốn đọc bằng 2 lệnh set địa chỉ X và Y, sau khi đọc giá trị địa chỉ page X và cột Y không thay đổi, do đó nếu đọc tiếp mà không di chuyển địa chỉ thì vẫn thu được giá trị cũ. 

III. AVR và Graphic LCD.

1. Trình tự giao tiếp GLCD. 

         So với Text LCD thì việc giao tiếp với GLCD dễ hơn nhiều vì GLCD có ít Instruction hơn, GLCD chỉ có một loại bộ nhớ là RAM tương ứng trực tiếp với màn hình hiển thị, GLCD không có cursor nên không cần set cursor, GLCD chỉ hỗ trợ giao tiếp 8 bit nên không cần bận tâm chọn mode, quá trình khởi động cho GLCD vì thể rất đơn giản bằng cách gọi lênh DISPLAY ON/OFF. Trong hình 5 tôi trình bày quá trình khởi động và sử dụng GLCD.

Flowchart

Hình 5. Trình tự giao tiếp với GLCD. 

       Sau khi khởi động GLCD bằng hàm DISPLAY ON chúng ta có thể set địa chỉ X và Y để ghi dữ liệu, thậm chí có thể ghi dữ liệu mà không cần set X, Y. Tuy nhiên, cần nhắc lại là có đến 2 chip KS0108 trên GLCD 128x64, vì vậy tất cả các quá trình đều phải thực hiện cho 2 chip.

2. AVR giao tiếp với GLCD trong WinAVR. 

       Phần này tôi trình bày cách điều khiển hiển thị GLCD 128x64 bằng vi điều khiển AVR trong môi trường C của WinAVR. Hình thức là một thư viện hàm giao tiếp GLCD trong 1 file header có tên là myGLCD.h. Các hàm trong thư viện bao gồm (chú ý là phần code trong List 0 không nằm trong file myGLCD.h): 

List 0. Các hàm có trong thư viện myGLCD.

List 0

       Trước khi viết các hàm giao tiếp LCD chúng ta cần định nghĩa một số macro và biến. Hãy tạo 1 file Header có tên myGLCD.h và viết các đoạn code bên dưới vào file này (bắt đầu từ List 1).

List 1. Định nghĩa các biến thay thế

List 1

        Do GLCD không hỗ trợ bộ font, nếu muốn hiển thị các ký tự chúng ta cần định nghĩa chúng trong một bảng font (tương tự trường hợp ma trận LED), file font.h đã được tạo trước và include vào thư viện myGLCD (dòng 2). Đồng thời, bộ font sẽ được chứa trong bộ nhớ chương trình (FLASH) nên cần các hàm hỗ trợ đọc FLASH, chúng ta include file pgmspace.h phục vụ cho việc này (dòng 3). 

       cbi và sbi là 2 macro được dụng để xóa và set 1 bit trong 1 thanh ghi. Ví dụ cbi(PORTA, 5) là xóa bit 5 trong thanh ghi PORTA về 0. Do WinAVR không hỗ trợ tuy xuất trực tiếp các bit nên cần định nghĩa 2 macro này hỗ trợ (dòng 5, 6). 

       Tám đường DATA sẽ được dành cho 1 PORT, các dòng 8, 9 và 10 định nghĩa PORT trên AVR dành cho DATA, trong ví dụ này là PORTB. Tương tự các đường điều khiển cũng nằm trên cùng 1 PORT, các dòng 14, 15, 16 định nghĩa PORT dành cho các đường điều khiển (PORTD chẳng hạn), sau đó chúng ta định nghĩa thứ tự chân trên PORT điều khiển kết nối với các chân EN, RW, RS, CS1 và CS2 của GLCD (xem các dòng từ 18 đến 22). Chúng ta định nghĩa tiếp 2 macro để kích hoạt và stop GLCD ở các dòng 25 và 26 vì các hoạt động này được dùng rất nhiều khi giao tiếp với GLCD. 

     Tiếp theo chúng ta định nghĩa 4 mã lệnh (Instruction code) của 4 hàm Display on/off, Set Address, Set page và Display Start Line mà tôi đã trình bày ở trên (các dòng từ 29 đến 32). Cuối cùng là định nghĩa vị trí bit BUSY khi đọc trạng thái GLCD.

     Sau phần định nghĩa chúng ta sẽ bắt đầu viết code truy cập GLCD, đoạn code trình bày trong List 2 chứa các hàm hỗ trợ.

List 2. Các hàm hỗ trợ.

List 2

      Hàm GLCD_Delay() thực hiện delay khoảng 16 chu kỳ máy, hàm này được dùng để chờ LGCD đọc hay ghi dữ liệu sau khi chân EN được kích. Một nét mới ở đây là tôi sử dụng ngôn ngữ ASM chèn vào C, dù chỉ là chèn hàm nop nhưng nó nói cho bạn biết rằng avr-gcc cho phép chúng ta chèn ASM, tôi sẽ trình bày chi tiết các ví dụ chèn ASM phức tạp hơn trong một bài khác.

        Hàm GLCD_OUT_Set() ở dòng 5 set các PORT giao tiếp trên AVR (DATA và Control) có hướng Ouput. Hàm GLCD_IN_Set() ở dòng 12 set các PORT giao tiếp có hướng Input (dùng khi đọc từ GLCD -> AVR). Hàm GLCD_SetSide(char Side) ở dòng 19 chọn chip KS0108 trái hoặc phải để thao tác, trong đó Side=1 thì một nửa GLCD bên phải được chọn bằng cách reset bit CS1=0 và CS2=1 (các dòng 22, 23), ngược lại nửa bên trái được kích hoạt, CS1=1, CS2=0 (dòng 26 và 27).

       List 3 trình bày phần code cho 4 hàm truy cập Instruction GLCD cơ bản viết lại cho các hàm Status Read, Display On/Off, Set Address, Set page và Display Start Line trích từ bảng 2. 

List 3. Các hàm truy cập Instruction.

List 3

       Tất cả các hàm trong List 3 đều truy cập Instruction nên chân RS luôn được kéo xuống mức thấp, trong 5 hàm trên, hàm wait_GLCD sử dụng Instruction đọc trạng thái từ GLCD nên chân RW sẽ được kéo lên cao, trong 4 hàm còn lại chân RW ở mức thấp. 

      Hàm wait_GLCD(void), cũng tương tự như hàm wait_LCD() trong trường hợp Text LCD, hàm này chờ GLCD rảnh bằng cách đọc trạng thái GLCD và kiểm tra bit BUSY, nếu BUSY bằng 1 thì GLCD đang bận, BUSY=0 tức GLCD rảnh. Các dòng 4, 5, 6 chuẩn bị các đường DATA, RS, RW cho quá trình đọc Instruction từ GLCD (RS=0, RW=0), ở dòng 8 chân EN được kéo lên cao bằng macro GLCD_ENABLE (định nghĩa trong list 1). Nhắc lại chức năng của chân EN, khi EN=1 GLCD bắt đầu quá trình giao tiếp do các chân RS, RW xác lập (trong trường hợp này là đọc Instruction từ GLCD), chúng ta cần chờ một khoảng thời gian ngắn cho GLCD đẩy thanh ghi trạng thái ra các đường DATA bằng hàm GLCD_Delay() trong dòng 9. Tiếp theo gọi GLCD_DISABLE để kéo chân EN xuống mức 0 để kết thúc quá trình đọc (một xung đã được tạo trên chân EN), và bắt đầu kiểm tra bit BUSY. Dòng 12 là một vòng lặp while kiểm tra xem nếu bit BUSY trong giá trị đọc về (giá trị đọc về chứa trong thanh ghi PIN của PORT DATA trên AVR), nếu BUSY=1 (bit_is_set…) vòng lặp tiếp tục với việc tạo một xung khác trên chân EN (các dòng ) rồi quay lại kiểm tra bit BUSY. Nếu BUSY bằng 0, GLCD đã rảnh, vòng lặp while được giải thoát, quá trình chờ kết thúc.

      Hàm GLCD_SetDISPLAY(uint8_t ON) cho phép GLCD hiển thị khi tham số ON=1, hoặc tắt khi tham số ON=0. Trước khi set GLCD chúng ta cần chờ cho GLCD rảnh bằng cách gọi hàm wait_GLCD() ở dòng 20, sau đó xác lập các chân RS, RW sẵn sàng cho quá trình gởi mã lệnh vào GLCD (dòng 21, 22 và 23). Trước khi kích hoạt quá trình, cần chuẩn bị mã lệnh sẵn sàng trên đường dữ liệu, dòng 25: GLCD_DATA_O=GLCD_DISPLAY+ON, trong đó GLCD_DISPLAY là mã lệnh của hàm Display On/Off được định nghĩa trong List 1, biến ON báo GLCD tắt hay mở. Sau khi mọi thứ đã sẵn sàng, một xung được tạo ra trên chân EN (các dòng từ 26 đến 28). Quá trình set Display thực hiện và kết thúc.

     Hàm void GLCD_SetYADDRESS(uint8_t Col) là hàm viết lại cho Insrtuction chọn địa chỉ Y (cột) cần thao tác, tham số Col trong hàm này chính là chỉ số cột, Col có giá trị từ 0 đến 63. Nội dung hàm này hoàn toàn giống hàm GLCD_SetDISPLAY, chỉ có một điểm khác duy nhất là mã hàm khác, mã GLCD_YADDRESS được dùng (xem dòng 36: GLCD_DATA_O = GLCD_YADDRESS+Col).

      Hàm void GLCD_SetXADDRESS(uint8_t Line) là hàm viết lại cho Insrtuction chọn địa chỉ X (page) cần thao tác, tham số Line trong hàm này chính là chỉ số page, Line có giá trị từ 0 đến 8. Nội dung hàm này hoàn toàn giống hàm GLCD_SetXADDRESS, nhưng mã GLCD_XADDRESS được dùng thay cho GLCD_YADDRESS,(xem dòng 36: GLCD_DATA_O = GLCD_XADDRESS+Line).

      Hàm void GLCD_StartLine(uint8_t Offset) là hàm viết lại cho Insrtuction “cuộn” GLCD, chỉ số Offset là giá trị “cuộn” lên. Xem lại ví dụ hình cuộn GLCD trong phần giải thích của lệnh Display Start Line, với trường hợp này hàm GLCD_StartLine(20) đã được gọi.

List 4 trình bày 2 hàm viết và đọc dữ liệu hiển thị lên GLCD.

List 4. Các hàm thao tác dữ liệu.

List 4

       Hai hàm trong list 4 thao tác dữ liệu hiển thị trên GLCD nên chân RS phải được set bằng 1.

      Hàm GLCD_WriteDATA(uint8_t DATA) ghi một byte vào RAM của KS0108, byte này cũng sẽ được hiển thị lên GLCD, vị trí ghi vào là vị trí hiện hành của con trỏ X và Y (ảnh hưởng bởi các quá trình ghi trước đó hoặc do các hàm set địa chỉ), tham số DATA là byte cần ghi. Nội dung bên trong hàm này cũng giống nhứ các hàm trong list 3. Điểm khác là chân RS được kéo lên để báo đây là quá trình thao tác dữ liệu (dòng 6: sbi(GLCD_CTRL_O, GLCD_RS)). Giá trị gởi đến GLCD chính là tham số DATA như trong dòng 8: GLCD_DATA_O=DATA.

       Hàm uint8_t GLCD_ReadDATA(void) đọc giá trị hiển thị trên từ GLCD vào AVR, chân RW cần được set lên 1 để báo quá trình này là đọc (dòng 22: sbi(GLCD_CTRL_O, GLCD_RW)). Chân EN được kích lên 1 trước (dòng 24: GLCD_ENABLE;) và chờ một khoảng thời gian ngắn trước khi đọc giá trị từ các đường DATA vào một biến tạm DATA như trong dòng 26: DATA=GLCD_DATA_I;. Sau khi trả giá trị về bằng dòng lệnh 30: return DATA, thì quá trịnh đọc kết thúc.

      Với các hàm đã tạo chúng ta đã có thể điều khiển để hiển thị GLCD, các chương trình con trong List 5 và List 6 sử dụng các hàm trên để thực hiện một số nhiệm vụ hiển thị cơ bản. Chúng ta gọi là các chương trình con mở rộng.

List 5. Các chương trình con mở rộng.

List 5

       Hàm void GLCD_Init(void) khởi động GLCD. Trước hết, chúng ta phải chọn chip KS0108 để khởi động, dòng 4: GLCD_SetSide(0) nghĩa là chọn chip KS0108 bên trái tức nửa trái GLCD.  Chúng ta khởi động nửa trái GLCD bằng việc cho phép hiển thị (dòng 5: GLCD_SetDISPLAY(1)), di chuyển con trỏ về vị trí đầu tiên trên GLCD với 2 hàm chọn địa chỉ ở các dòng 6 và 7, chọn giá trị cuộn là 0 ở dòng 8: GLCD_StartLine(0). Sau đó lặp lại quá trình khởi động cho nửa phải của GLCD (xem các dòng từ 10 đến 14).

       Hàm void GLCD_GotoXY(uint8_t Line, uint8_t Col) di chuyển con trỏ hiển thị đến địa chỉ X và Y. Tham số Line là địa chỉ X (tức là page, giá trị từ 0 đến 7), tham số Col là địa chỉ Y hay chính là cột. Hàm này cho phép di chuyển trên toàn bộ GLCD, nghĩa là biến Col có khoảng giá trị từ 0 đến 127, vì thế trước hết chúng ta phải xác định vị trí cần duy chuyển đến thuộc nửa nào của GLCD, nếu Col<64 thì vị trí đó thuộc nửa trái, ngược lại nó thuộc về nửa phải. Dòng 19 chúng ta chia Col cho 64 và gán phần nguyên kết quả cho 1 biến tạm tên là Side (Side=Col/64 ), rõ ràng nếu Col<64 thì Side=0, ngược lại Side=1. Biến Side được dùng làm tham số cho hàm GLCD_SetSide(Side) ở dòng 20, với cách thực hiện này chúng ta đã tự động chọn nửa GLCD mà điểm cần di chuyển đến thuộc vào. Do hàm chọn địa chỉ Y (hàm GLCD_SetYADDRESS xét ở trên) chỉ chọn địa chỉ trong phạm vi 1 nửa LCD, nên chúng ta cần cập nhật lại giá trị của cột Col, dòng 21 thực hiện việc này: Col -= 64*Side. Sau dòng 21, giá trị Col được cập nhật lại từ 0 đến 63 và được chọn làm cột khi hàm GLCD_SetYADDRESS(Col) ở dòng 22 được gọi. Cuối cùng là chọn địa chỉ X ở dòng 23: GLCD_SetXADDRESS(Line).

       Hàm void GLCD_Clr(void) xóa toàn bộ màn hình GLCD (cả 2 nửa GLCD). Mấu chốt của việc xóa GLCD là viết giá trị 0 vào tất cả các vị trí trong RAM, câu lệnh: GLCD_WriteDATA(0) ở 2 dòng 29 và 33 thực hiện điều này. Quá trình xóa được thực hiện trên từng chip KS0108, có 2 vòng vặp for được dùng là vì thế, chú ý dòng lệnh 28: GLCD_GotoXY(Line,0) đưa con trỏ về cột đầu của page thứ “Line”, nửa trái GLCD. Trong khi đó, dòng lệnh 32: GLCD_GotoXY(Line,64) đưa con trỏ về cột đầu của page thứ “Line”, nửa phải GLCD (cột 64 của GLCD là cột đầu tiên của nửa bên phải).

List 6. Các chương trình con mở rộng (tt)..

List 6

       Đây là 3 chương trình con cuối cùng trong thư viện myGLCD. Trong đó có 2 hàm in các ký có kích thước 7x8 (7 cột, 8 dòng) được định nghĩa trong bảng font và 1 hàm in toàn bộ màn hình GLCD với một hình kích thước 128x64. 

      Hàm void GLCD_PutChar78(uint8_t Line, uint8_t Col, uint8_t chr) cho phép in ký tự có mã ascii là biến “chr”, biến “Line” là địa chỉ X (0 đến 7) và biến Col là địa chỉ cột Y (0 đến 127). Phần phức tạp nhất trong chương trình con này là việc xét trường hợp có sự chuyển bên (trái qua phải) khi in. Vì mỗi ký tự được định nghĩa bằng 7 bytes trong bảng font, tương ứng với 7 cột trên GLCD, nếu chúng ta muốn in ký tự tại vị trí 60 trên GLCD, các byte thứ 0 1, 2, 3 nằm ở vị trí cột 60, 61, 62 và 63 của nửa trái trong khi các byte thứ 4, 5 và 6 lại nằm ở các cột 0, 1 và 2 của nửa bên phải. Chúng ta phải nhận ra sự chuyển bên này để chuyển chip KS0108 cần thao tác. Chúng ta chia quá trình in ra 2 trường hợp, trường hợp có sự chuyển bên và trường hợp còn lại không chuyển bên (ký tự nằm trọn bên trái hoặc phải). Cấu trúc If dùng trong dòng 4 kiểm tra xem có sự chuyển bên xảy ra hay không: if ((Col>57) && (Col<64)),  nếu cột Col lớn hơn 57 và nhỏ hơn 63 thì sẽ có một sự chuyển bên xảy ra (vì 1 ký tự chiếm 7 cột trên GLCD). Chia quá trình in thành 2 vòng lặp for, vòng for thứ nhất (dòng 6) in từ vị trí Col đến vị trí cột của nửa trái và vòng lặp for thứ 2 ở dòng 9 in từ cột đầu tiên của nửa GLCD bên phải đến byte cuối cùng của ký tự cần in. Trường hợp ngược lại, không có sự chuyển bên xảy ra, chúng ta in bình thường (xem các dòng từ 12 đến 15). Chú ý là dữ liệu ghi vào GLCD lấy từ bảng font7x8 được định nghĩa trong file font.h, bảng font được viết sẵn trong bộ nhớ FLASH của AVR, việc đọc nội dung FLASH thực hiện bằng hàm pgm_read_byte, bạn xem lại bài điều khiển ma trận LED để hiểu thêm. 

       Hàm void GLCD_Print78(uint8_t Line, uint8_t Col, char* str) cho phép in một chuỗi ký tự hay 1 câu lên GLCD, hàm này cũng giống hàm in chuỗi mà chúng ta đã thực hiện trong trường hợp của Text LCD (modified code), một điểm khác tôi thêm vào là cho phep xuống dòng nếu câu cần in vượt quá 1 dòng. Các câu lệnh bên trong điều kiện if (dòng 23 đến 27) thực hiện xuống dòng nếu cần thiết. Quá trình in sau đó diễn ra bình thường bằng cách gọi hàm GLCD_PutChar78.

       Cuối cùng là hàm void GLCD_PutBMP(char *bmp) thực hiện in một hình có kích thước 128x64 được định nghĩa trước lên toàn bộ màn hình GLCD (in đè). Quá trình in cũng khá đơn giản với việc đọc nội dung hình trong FLASH và gởi đến GLCD. Cần chia thành 2 quá trình in cho 2 nửa trái và phải (2 vòng lặp for trong 2 dòng 38 và 43). Dữ liệu hình được ghi trong FLASH có định kích thước 128x8 pages= 1024 bytes, định dạng là 1 mảng có 1024 phần tử, mỗi phần tử là 1 con số dạng byte, mỗi số tương ứng 8 chấm của 1 cột trong 1 page. Các con số được sắp xếp thành 8 dòng tương ứng 8 pages, mỗi dòng có 128 phần tử tương ứng 128 cột GLCD.

       GLCD có khả năng tùy biến hiển thị cao, đó là cơ hội cho bạn thể hiện sự sáng tạo ~, trong thư viện myGLCD tôi chỉ trình bày một số chương trình con cơ bản, phần còn lại thuộc về bạn. Hãy sử dụng các hàm truy xuất trong myGLCD để viết các chương trình con hiển thị khác như vẽ đường thẳng, đường tròn, hàm sine, cosine hay bất kỳ hàm số nào…Hope to hear from you soon.

IV. Ví dụ điều khiển Graphic LCD bằng thư viện myGLCD.

        Phần này tôi sẽ minh họa cách sử dụng thư viện myGLCD.h để in trực tiếp dữ liệu lên GLCD, hiển thị các ký tự trong bảng font7x8 và hình ảnh lên GLCD. Sử dụng phần mềm Proteus vẽ một mạch điện gồm 1 GLCD 128x64 (keyword: LGM12641BS1R), 1 chip Atmega32 và 1 biến trở (keyword: POT-LIN) như trong hình 6. Tạo 1 Project bằng WinAVR có tên là myGLCD và tạo file source là main.c, tạo Makefile với khai báo sữ dụng chip ATmega32 và clock 8MHz. Copy file myGLCD.h và font.h vào thư mục của Project mới tạo. Viết code cho file main.c như trong list 7. Chú ý các định nghĩa chân kết nối với LCD trong phần đầu file myGLCD.h phải giống với kết nối thật trong hình 6.

GLCD demo

Hình 6. Mạch điện mô phỏng Graphic LCD với AVR.  

List 7. Chương trình demo giao tiếp GLCD.

List demo

        Để sử dụng thư viện myGLCD, chúng ta cần include file myGLCD.h vào Project như trong dòng 4, #include "myGLCD.h". Hai dòng 9 và 10 thực hiện khởi động và xóa LCD. Tôi thực hiện 4 demo in lên GLCD. Trong các dòng từ 12 đến 17 thực hiện ghi trực tiếp giá trị lên GLCD bằng hàm GLCD_WriteDATA, kết quả là 1 dãy các chấm có độ rộng 8 bit nằm ở page 4 của GLCD (xem hình bên dưới). Chú ý là để in hết cả chiều ngang của GLCD cần thực hiện 2 lần in trên 2 nửa GLCD. 

result1

       Các dòng lệnh từ 21 đến 27 thực hiện in 97 ký tự trong bảng font7x8 bắt đầu bằng mã ascii 33 (ký tự “!”), biến Line là địa chỉ page được khởi tạo bằng 0 khi khai báo trong dòng 7. Biến Col là địa chỉ cột, Col cũng được khởi tạo bằng 0. Dòng 32 in ký tự có mã “i” lên GLCD tại vị trí page=Line, cột=Col. Sau khi một ký tự được in, Col sẽ được tăng lên  8 vị trí (dòng 24), chúng ta dành 8 cột trên GLCD cho một ký tự 7x8 để tránh các ký tự “dính” với nhau. Nếu Col lớn hơn 127 thì một quá trình xuống dòng cần thực hiện, khi đó reset Col về 0 và tăng biến Line thêm 1 (dòng 25). Các ký tự sẽ được in lần lượt trên GLCD với 1 khoảng delay.

      Các dòng từ 31 đến 35 mô tả cách dùng hàm GLCD_Print78 để in các chuỗi ký tự hay các câu. Dòng 23 in từ “code” lên GLCD tại vị trí page=4, cột=20. Chú ý hàm sprintf trong dòng 33, đây là một hàm của ngôn ngữ C, hàm này cho phép chuyển một số thành một chuỗi các ký tự, trong ví dụ này tôi thực hiện chuyển số 8205 thành chuỗi “8205”, kết quả chứa trong biến “dis”, biến này là 1 mảng các ký tự hay con trỏ đến mảng các ký tự. Sau đó, dòng 34 in chuỗi “dis” lên GLCD.

      Demo cuối cùng là in 1 hình 128x64 lên GLCD bằng hàm GLCD_PutBMP(hiGLCD) và sau đó thực hiện animation (một kiểu hoạt hình) bằng hàm GLCD_StartLine. Dòng 38 in một hình có tên hiGLCD được định nghĩa trước trong file font.h ra GLCD. Các dòng 40 đến 44 cuộn màn hình GLCD lên trên để thực hiện animation. Biến i là biến offset được cho chạy từ 1 đến 63, sau mỗi lần cuộn chúng ta delay một khoảng thời gian ngắn để thấy GLCD “cuộn”.

      Hãy tham khảo thêm bài giới thiệu phần mềm G.Edit để biết cách tạo code hình ảnh cho Graphic LCD.