You are currently viewing FRDM-KL46Z #4: Ghép nối với LCD

FRDM-KL46Z #4: Ghép nối với LCD

Màn hiển thị LCD là một thiết bị ngoại vi được sử dụng phổ biến trong các hệ thống nhúng. Bài viết này miêu tả các chế độ hoạt động của màn hiển thị LCD và cách ghép nối và lập trình LCD với bo mạch phát triển FRDM-KL46Z.

1.        Một số ưu điểm của LCD

Trong những năm gần đây, hiển thị LCD đang thay thế dần các bảng hiển thị bằng đèn LED (đèn LED bảy đoạn hoặc đèn LED nhiều đoạn khác). Điều này là do các lý do sau đây:

  1. Giá LCD ngày cảng giảm.
  2. Khả năng hiển thị số, ký tự và đồ họa của LCD. Điều này trái ngược với đèn LED chỉ hiển thị được các số và một vài ký tự. (Các tấm nền OLED mới tương đối đắt hơn nhiều ngoại trừ những tấm rất nhỏ. Nhưng giá của chúng đang giảm. Giao diện ghép nối và lập trình cho OLED tương tự như LCD đồ họa.)
  3. Tích hợp bộ điều khiển làm tươi thông tin vào màn hình LCD, do đó làm giảm tải cho CPU khỏi nhiệm vụ làm tươi màn hình LCD.
  4. Dễ lập trình cho cả các ký tự và đồ họa.
  5. Mức tiêu thụ điện năng cực thấp của LCD (khi không sử dụng đèn nền).

2.        Giao diện ghép nối của LCD

      Trong nhiều năm qua, bộ điều khiển LCD Hitachi HD44780 được sử dụng chiếm ưu thế trong các mô-đun LCD hiển thị ký tự. Ngay cả hiện nay, hầu hết các mô-đun LCD hiển thị ký tự vẫn sử dụng bộ điều khiển HD44780 hoặc một biến thể của nó. Bộ điều khiển HD44780 có giao diện ghép nối với bộ vi xử lý gồm 14 chân. Chức năng của mỗi chân trong giao diện này được thể hiện trong Bảng 4‑1.

Bảng 4‑1. Chức năng các chân trong giao diện ghép nối của LCD.

Chân số Ký hiệu Hướng I/O Miêu tả
1 VSS Nguồn đất
2 VCC Nguồn +5V
3 VEE Tín hiệu nguồn để điều khiển độ tương phan
4 RS I Tín hiệu lựa chọn thanh ghi:

–     RS=0: chọn thanh ghi lệnh

–     RS=1: chọn thanh ghi dữ liệu

5 R/W I Tín hiệu chọn chế độ đọc/viết:

–     R/W=1: đọc

–     R/W=0: viết

6 E I Tín hiệu cho phép (Enable)
7-14 DB0-DB7 I/O Bus dữ liệu 8 bit

 

  • Nguồn VCC, VSS và VEE: trong khi VCC và VSS cung cấp nguồn nuôi +5V và đất tương ứng thì VEE được sử dụng để điều khiển độ tương phản LCD.
  • Tín hiệu chọn thanh ghi RS (Register Select): Có hai loại thanh ghi bên trong LCD và chân RS được sử dụng để lựa chọn chúng như sau. Nếu RS = 0 thì thanh ghi mã lệnh được chọn, cho phép người dùng gửi lệnh điều khiển như xóa hiển thị, đưa con trỏ đầu hàng, v.v. hoặc truy vấn bit trạng thái bận của bộ điều khiển. Nếu RS = 1 thì thanh ghi dữ liệu được chọn, cho phép người dùng gửi dữ liệu được hiển thị trên LCD (hoặc để lấy dữ liệu từ bộ điều khiển LCD).
  • Tín hiệu đọc/viết R/W (Read/Write): Đầu vào R/W cho phép người dùng chọn giữa chế độ ghi thông tin vào bộ điều khiển LCD hoặc đọc thông tin từ nó. R/W = 1 chọn chế độ đọc và R/W = 0 chọn chế độ viết.
  • Tín hiệu cho phép E (Enable): Chân này được LCD sử dụng để chốt thông tin biểu diễn trên các chân dữ liệu vào trong LCD. Khi dữ liệu được cung cấp trên các chân dữ liệu ổn định, một xung (Thấp-Cao-Thấp) phải được áp dụng trên chân này sẽ chốt dữ liệu từ các chân dữ liệu vào trong LCD. Theo bảng dữ liệu của Hitachi thì xung này phải rộng tối thiểu 230 ns,.
  • Các tín hiệu D0-D7: là các chân dữ liệu 8 bit được sử dụng để gửi thông tin đến LCD hoặc đọc nội dung của các thanh ghi bên trong LCD. Bộ điều khiển LCD có khả năng hoạt động ở chế độ dữ liệu 4 bit với chỉ D4-D7 được sử dụng. Chúng ta sẽ thảo luận về điều này chi tiết hơn ở phần sau. Để hiển thị các chữ ký tự và chữ số trên LCD, chúng ta cần gửi mã ASCII của các chữ cái A-Z, a-z, số 0-9 và dấu chấm câu trên các chân này trong khi đặt RS = 1. Các mã lệnh cũng có thể được gửi tới LCD thông qua các chân này để xóa màn hình, đưa con trỏ về vị trí ban đầu hoặc nhấp nháy con trỏ. Bảng 4‑2 liệt kê một số mã lệnh thường được sử dụng. Để biết mã lệnh chi tiết xem Bảng 4‑3.

Bảng 4‑2. Một vài mã lệnh được dùng phổ biến của LCD.

Mã (Hex) Lệnh gửi tới thanh ghi lệnh của LCD
1 Xóa màn hình
2 Đưa con trỏ về đầu dòng
6 Tăng vị trí con trỏ (Dịch con trỏ sang phải)
F Bật hiển thị, nháy con trỏ
80 Đưa con trỏ về đầu dòng đầu tiên
C0 Đưa con trỏ về đầu dòng thứ hai
38 Thiết lập chế độ hiển thị 2 dòng ký tự kích thước 5×7 (8 bit dữ liệu gồm D0 tới D7)
28 Thiết lập chế độ hiển thị 2 dòng ký tự kích thước 5×7 (4 bit dữ liệu gồm D4 tới D7)

Hình 4‑1 và Hình 4‑2 là các giản đồ thời gian mô tả thao tác viết và đọc LCD.

Giản đồ thời gian mô tả thao tác viết LCD [19]

Hình 4‑1. Giản đồ thời gian mô tả thao tác viết LCD [19].Giản đồ thời gian mô tả thao tác đọc LCD [19]

Hình 4‑2. Giản đồ thời gian mô tả thao tác đọc LCD [19].

Lưu ý rằng thao tác viết xảy ra trên sườn chuyển tiếp 1-sang-0 của tín hiệu trên chân E. Bộ vi điều khiển phải tạo ra dữ liệu sẵn sàng và ổn định trên các đường dữ liệu trước khi tạo ra sườn chuyển tiếp 1-sang-0 của tín hiệu trên chân E để thỏa mãn yêu cầu về thời gian thiết lập như chỉ ra trong Hình 4‑1.

Hoạt động đọc được kích hoạt bởi xung 0-sang-1 của tín hiệu trên chân E. Sau thời gian trễ, bộ điều khiển LCD sẽ phải tạo ra dữ liệu sẵn sàng trên bus dữ liệu nếu đường tín hiệu R/W ở mức cao. Bộ vi điều khiển nên đọc dữ liệu từ các bus dữ liệu trước khi hạ tín hiệu trên chân E xuống mức thấp.

Bảng 4‑3. Chi tiết các mã lệnh điều khiển LCD Hitachi HD44780 [19].

Lệnh RS R/W D7-D0 Miêu tả Thời gian thực thi (lớn nhất)
Xóa màn hình 0 0 00000001 Xóa toàn bộ màn hình và đặt giá trị bộ đếm địa chỉ của DDRM tới 0 1,64 ms
Trở về đầu dòng 0 0 0000001- Đặt bộ đếm địa chỉ DD RAM tới 0, nội dung DD RAM duy trì không đổi; 1,64 ms
Đặt chế độ nhập 0 0 000001[I/D]S Thiết lập hướng di chuyển con trỏ và hướng dịch chuyển màn hình hiển thị. 40sµs
Điều khiển mở/tắt hiển thị 0 0 00001DCB Thiết lập mở/tắt toàn bộ màn hình hiển thị (D), mở/tắt con trỏ (C), nháy con trỏ ở vị trí hiện tại (B) 40sµs
Dịch chuyển con trỏ hoặc màn hình hiển thị 0 0 0001[S/C][R/L]– Di chuyển con trỏ và dịch chuyển màn hình hiển thị mà không làm thay đổi nội dung của DD RAM 40sµs
Thiết lập chức năng 0 0 001[DL]NF– Đặt độ dài dữ liệu của giao diện ghép nối (DL), số dòng hiển thị trên LCD (N), kiểu font của các ký tự (F) 40sµs
Thiết lập địa chỉ của CG RAM 0 0 01[AGC(5-0)] Thiết lập địa chỉ của CG RAM. Dữ liệu của CG RAM được gửi và được nhận sau thiết laaoj này. 40sµs
Thiết lập địa chỉ của DD RAM 0 0 1[ADD(6-0)] Thiết lập địa chỉ của DD RAM. Sau thiết lập này, dữ liệu của DD RAM có thể được gửi và nhận 40sµs
Đọc địa chỉ và cờ trạng thái bận 0 1 BF[AC(6-0)] Đọc cờ trạng thái hoạt động bên trong LCD (BF) và đọc nội dung của bộ đếm địa chỉ 40sµs
Viết  dữ liệu tới DD RAM và CG RAM 1 0 Dữ liệu cần viết Viết  dữ liệu tới DD RAM và CG RAM 40sµs
Đọc dữ liệu từ DD RAM và CG RAM 1 1 Dữ liệu cần đọc Đọc dữ liệu từ DD RAM và CG RAM 40sµs

Chú thích:

  • DD RAM (Display Data RAM): bộ nhớ dữ liệu hiển thị
  • CG RAM (Character Generator RAM): bộ nhớ đệm cho máy tạo ký tự
  • AGC (CG RAM Address): địa chỉ CG RAM
  • ADD (DD RAM Address): địa chỉ DDRAM cũng là địa chỉ của con trỏ
  • AC (Addrees Counter): bộ đếm địa chỉ được sử dụng cho đếm địa chỉ của cả DD RAM và CG RAM
  • I/D: 1 – tăng, 0 – giảm
  • S = 1: cho phép dịch màn hình hiển thị
  • S/C: 1 – dịch màn hiển thị, 0 – di chuyển con trỏ chuột
  • R/L: 1 – dịch sang phải, 0 – dịch sang trái
  • DL (Data Length): 1 – 8 bit, 0 – 4 bit
  • N: 1 – 2 dòng, 0 – 1 dòng
  • F: 1 – phông 5×10 điểm, 0 – phông 5×7 điểm
  • BF: 1 – đang bận hoạt động, 0 – có thể nhận lệnh

2.1.     Gửi lệnh tới LCD

Để gửi một lệnh bất kỳ tới LCD cần đặt chân RS=0, R/W=0 và tạo ra một xung trên chân E để cho phép chốt lệnh vào bên trong LCD. Hình 4‑3 mô tả sơ đồi ghép nối mô-đun LCD tới vi điều khiển.

Ghép nối vi điều khiển với mô-đun LCD

Hình 4‑3. Ghép nối vi điều khiển với mô-đun LCD.

Kết nối trong Hình 4‑3 được thực hiện cụ thể với bo mạch FRDM-KL46Z như sau:

  1. Các chân dữ liệu D0 đến D7 của LCD được kết nối với chân 16 đến 23 của cổng PORTD của vi điều khiển một cách tương ứng.
  2. Chân RS của LCD được kết nối với chân 2 của PORTA của vi điều khiển.
  3. Chân R/W của LCD được kết nối với chân 4 của PORTA của vi điều khiển.
  4. Chân E của LCD được kết nối với chân 5 của PORTA của vi điều khiển.
  5. Cả hai cổng E và A được cấu hình là cổng đầu ra.

2.2.     Gửi dữ liệu lên màn hình LCD

Để gửi dữ liệu tới màn hình LCD để hiển thị, chúng ta phải đặt các chân RS = 1, R/W = 0, đồng thời tạo ra một xung trên chân E để chốt dữ liệu vào LCD.

Do đặc tính tiêu thụ năng lượng cực thấp của bộ điều khiển LCD khiến nó chạy chậm hơn nhiều so với vi điều khiển. Viết hai lệnh đầu tiên trong Bảng 3-2 cần tới 1,64 ms để hoàn thành còn viết tất cả các lệnh khác và viết dữ liệu mất tới 40µs (trong khi ở tốc độ xung nhịp cao nhất MKL46Z4 có thể thực hiện hơn 1.000 lệnh trong 40µs). Do đó, sau khi một lệnh hoặc dữ liệu được ghi vào bộ điều khiển LCD, chương trình trên vi điều khiển phải đợi cho đến khi bộ điều khiển LCD sẵn sàng trước khi phát đi lệnh/dữ liệu tiếp theo nếu không lệnh/dữ liệu thứ hai sẽ bị bỏ qua. Một cách đơn giản (mặc dù không hiệu quả) là để trì hoãn vi điều khiển trong thời gian tối thiểu bằng thời gian để lệnh trước đó hoàn thành. Chúng ta sẽ sử dụng phương pháp này trong các ví dụ sau. Tất cả các ví dụ trong chương này sử dụng thời gian thực thi lệnh được liệt kê trong Bảng 4‑3 để tạo ra thời gian trễ trong chương trình. Bạn có thể dễ dàng điều chỉnh thời gian trễ này trong chương trình để phù hợp với mô-đun LCD bạn sử dụng.

Chương trình 41: Hiển thị thông báo trên LCD bằng chế độ dữ liệu 8-bit.
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

/* LCD_8bit.c: Initialize and display “Hello” on the LCD using 8-bit data mode.

* Data pins use Port E, control pins use Port A.

* This program does not poll the status of the LCD.      

* It uses delay to wait out the time LCD controller is busy.

* Timing is more relax than the HD44780 datasheet to accommodate the

* variations among the LCD modules.

* You may want to adjust the amount of delay for your LCD controller.

*/

#include <MKL46Z4.H>

#define RS 0x04  /* PTA2 mask */

#define RW 0x10  /* PTA4 mask */

#define EN 0x20 /* PTA5 mask */

void delayMs(int n);

void LCD_command(unsigned char command);

void LCD_data(unsigned char data);

void LCD_init(void);

int main(void)

{

LCD_init();

for(;;)

{

LCD_command(1); /* clear display */

delayMs(500);

LCD_command(0x80); /* set cursor at first line */

LCD_data(‘H’); /* write the word */

LCD_data(‘e’);

LCD_data(‘l’);

LCD_data(‘l’);

LCD_data(‘o’);

delayMs(500);

}

}

void LCD_init(void)

{

SIM->SCGC5 |= (1u << 13); /* enable clock to Port E */

PORTE->PCR[0] = (1u << 8); /* make PTE0 pin as GPIO */

PORTE->PCR[1] =  (1u << 8); /* make PTE1 pin as GPIO */

PORTE->PCR[2] =  (1u << 8); /* make PTE2 pin as GPIO */

PORTE->PCR[3] =  (1u << 8); /* make PTE3 pin as GPIO */

PORTE->PCR[4] =  (1u << 8); /* make PTE4 pin as GPIO */

PORTE->PCR[5] =  (1u << 8); /* make PTE5 pin as GPIO */

PORTE->PCR[6] =  (1u << 8); /* make PTE6 pin as GPIO */

PORTE->PCR[7] =  (1u << 8); /* make PTE7 pin as GPIO */

PTE->PDDR = (0xFFu << 16); /* make PTE23-16 as output pins */

SIM->SCGC5 |= 0x0200; /* enable clock to Port A */

PORTA->PCR[2] = 0x100; /* make PTA2 pin as GPIO */

PORTA->PCR[4] = 0x100; /* make PTA4 pin as GPIO */

PORTA->PCR[5] = 0x100; /* make PTA5 pin as GPIO */

PTA->PDDR |= 0x34; /* make PTA5, 4, 2 as output pins */

delayMs(30); /* initialization sequence */

LCD_command(0x30);

delayMs(10);

LCD_command(0x30);

delayMs(1);

LCD_command(0x30);

LCD_command(0x38); /* set 8-bit data, 2-line, 5×7 font */

LCD_command(0x06); /* move cursor right */

LCD_command(0x01); /* clear screen, move cursor to home */

LCD_command(0x0F); /* turn on display, cursor blinking */

}

void LCD_command(unsigned char command)

{

PTA->PCOR = RS | RW; /* RS = 0, R/W = 0 */

PTE->PDOR = (command << 16); // PTE->PDOR(23-16)=Command

PTA->PSOR = EN; /* pulse E */

delayMs(0);

PTA->PCOR = EN;

if (command < 4)

delayMs(4); /* command 1 and 2 needs up to 1.64ms */

else

delayMs(1); /* all others 40 us */

}

void LCD_data(unsigned char data)

{

PTA->PSOR = RS; /* RS = 1, R/W = 0 */

PTA->PCOR = RW;

PTE->PDOR = (data << 16);

PTA->PSOR = EN; /* pulse E */

delayMs(0);

PTA->PCOR = EN;

delayMs(1);

}

/* Delay n milliseconds

* The CPU core clock is set to MCGFLLCLK at 41.94 MHz in SystemInit().

*/

void delayMs(int n) {

int i;

int j;

for(i = 0 ; i < n; i++)

for(j = 0 ; j < 7000; j++) { }

}

Kiểm tra cờ trạng thái bận của LCD: Các chương trình trên đã chèn vào một khoảng thời gian chờ trước khi phát đi dữ liệu hoặc lệnh tiếp theo. Điều này cho phép LCD có đủ thời gian để sẵn sàng chấp nhận dữ liệu tiếp theo. Tuy nhiên, LCD có một cờ báo trạng thái bận. Chúng ta có thể giám sát cờ bận này và chỉ phát đi dữ liệu tiếp theo khi LCD sẵn sàng. Điều này sẽ tăng tốc chương trình và không lãng phí thời gian thực thi của vi điều khiển. Để kiểm tra cờ bận, chúng ta phải đọc thanh ghi lệnh (bằng cách thiết lập R/W = 1 và RS = 0). Cờ trạng thái bận là bit D7 của thanh ghi đó. Do đó, khi đọc được D7 = 1 (cờ bận = 1) chứng tỏ LCD đang bận thực hiện các hoạt động nội bộ và sẽ không chấp nhận bất kỳ thông tin mới nào. Khi đọc được D7 = 0 chứng tỏ LCD sẵn sàng nhận thông tin mới.

Cách giám sát trạng thái cờ bận ở trên yêu cầu phải chuyển hướng của cổng (tức cổng PORTE) được kết nối với bus dữ liệu của LCD sang chế độ đầu vào khi đọc kiểm tra thanh ghi trạng thái, sau đó chuyển hướng cổng trở lại chế độ đầu ra để gửi lệnh tiếp theo. Nếu hướng cổng không chính xác, nó có thể làm hỏng vi điều khiển hoặc mô-đun LCD. Chương trình tiếp theo sử dụng cơ chế giám sát cờ bận trong thanh ghi trạng thái của LCD.

Chương trình 42: Hiển thị thông báo trên LCD bằng chế độ dữ liệu 8 bit và hỏi vòng thanh ghi trạng thái bận của LCD
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

/* LCD_8bit_polling.c: Initialize and display “hello” on the LCD using 8-bit data mode.

* Data pins use Port D, control pins use Port A.

* Polling of the busy bit of the LCD status bit is used for timing.

*/

#include <MKL46Z4.H>

#define RS 0x04 /* PTA2 mask */

#define RW 0x10 /* PTA4 mask */

#define EN 0x20 /* PTA5 mask */

void delayMs(int n);

void LCD_command(unsigned char command);

void LCD_command_noWait(unsigned char command);

void LCD_data(unsigned char data);

void LCD_init(void);

void LCD_ready(void);

int main(void)

{

LCD_init();

for(;;)

{

LCD_command(1); /* clear display */

delayMs(500);

LCD_command(0xC0); /* set cursor at 2nd line */

LCD_data(‘h’); /* write the word on LCD */

LCD_data(‘e’);

LCD_data(‘l’);

LCD_data(‘l’);

LCD_data(‘o’);

delayMs(500);

}

}

void LCD_init(void)

{

SIM->SCGC5 |= (1u << 13); /* enable clock to Port E */

PORTDE->PCR[0] = 0x100; /* make PTE0 pin as GPIO */

PORTDE->PCR[1] = 0x100; /* make PTE1 pin as GPIO */

PORTDE->PCR[2] = 0x100; /* make PTE2 pin as GPIO */

PORTDE->PCR[3] = 0x100; /* make PTE3 pin as GPIO */

PORTDE->PCR[4] = 0x100; /* make PTE4 pin as GPIO */

PORTDE->PCR[5] = 0x100; /* make PTE5 pin as GPIO */

PORTDE->PCR[6] = 0x100; /* make PTE6 pin as GPIO */

PORTDE->PCR[7] = 0x100; /* make PTE7 pin as GPIO */

PTE->PDDR = (0xFF << 16); /* make PTE23-16 as output pins */

SIM->SCGC5 |= 0x0200; /* enable clock to Port A */

PORTA->PCR[2] = 0x100; /* make PTA2 pin as GPIO */

PORTA->PCR[4] = 0x100; /* make PTA4 pin as GPIO */

PORTA->PCR[5] = 0x100; /* make PTA5 pin as GPIO */

PTA->PDDR |= 0x34; /* make PTA5, 4, 2 as output pins */

delayMs(20); /* initialization sequence */

LCD_command_noWait(0x30); /* LCD does not respond to status poll */

delayMs(5);

LCD_command_noWait(0x30);

delayMs(1);

LCD_command_noWait(0x30);

LCD_command(0x38); /* set 8-bit data, 2-line, 5×7 font */

LCD_command(0x06); /* move cursor right */

LCD_command(0x01); /* clear screen, move cursor to home */

LCD_command(0x0F); /* turn on display, cursor blinking */

}

/* This function waits until LCD controller is ready to

* accept a new command/data before returns.

*/

void LCD_ready(void)

{

char status;

PTE->PDDR = 0; /* PORTDE input */

PTA->PCOR = RS; /* RS = 0 for status */

PTA->PSOR = RW; /* R/W = 1, LCD output */

do { /* stay in the loop until it is not busy */

PTA->PSOR = EN; /* raise E */

delayMs(0);

status = PTE->PDIR; /* read status register */

PTA->PCOR = EN;

delayMs(0); /* clear E */

} while (status & 0x80); /* check busy bit */

PTA->PCOR = RW; /* R/W = 0, LCD input */

PTE->PDDR = (0xFF << 1); /* PORTDE output */

}

void LCD_command(unsigned char command)

{

LCD_ready(); /* wait until LCD is ready */

PTA->PCOR = RS | RW; /* RS = 0, R/W = 0 */

PTE->PDOR = (command << 16);

PTA->PSOR = EN; /* pulse E */

delayMs(0);

PTA->PCOR = EN;

}

void LCD_command_noWait(unsigned char command)

{

PTA->PCOR = RS | RW; /* RS = 0, R/W = 0 */

PTE->PDOR = (command << 16);

PTA->PSOR = EN; /* pulse E */

delayMs(0);

PTA->PCOR = EN;

}

void LCD_data(unsigned char data)

{

LCD_ready(); /* wait until LCD is ready */

PTA->PSOR = RS; /* RS = 1, R/W = 0 */

PTA->PCOR = RW;

PTE->PDOR = (data<<16);

PTA->PSOR = EN; /* pulse E */

delayMs(0);

PTA->PCOR = EN;

}

/* Delay n milliseconds

* The CPU core clock is set to MCGFLLCLK at 41.94 MHz in SystemInit().

*/

void delayMs(int n) {

int i, j;

for(i = 0 ; i < n; i++)

for(j = 0 ; j < 7000; j++) { }

}

2.3.     Giao diện ghép nối LCD 4 bit

Để tiết kiệm số lượng chân vi điều khiển được sử dụng để ghép nối với LCD, chúng ta có thể sử dụng chế độ ghép nối dữ liệu 4 bit thay vì 8 bit. Trong chế độ dữ liệu 4 bit, chúng ta chỉ cần kết nối các chân D7-D4 của LCD với vi điều khiển. Cùng với ba đường điều khiển, giao diện ghép nối giữa vi điều khiển và mô-đun LCD sẽ cần tổng cộng bảy chân cổng của vi điều khiển như Hình 4‑4.

Với chế độ ghép nối dữ liệu 4 bit, vi điều khiển cần đưa ra các lệnh để đặt bộ điều khiển LCD vào chế độ 4 bit trong quá trình khởi tạo. Điều này được thực hiện bằng cách viết mã lệnh 0x20 tới thanh ghi điều khiển của LCD trong Chương trình 4‑3. Sau đó, mọi lệnh hoặc dữ liệu cần được chia thành hai cụm 4 bit và lần lượt gửi tới vi điều khiển theo thứ tự 4 bit cao trước, 4 bit thấp sau. Trong Chương trình 4‑3, cụm 4 bit cao được trích xuất bằng lệnh “& 0xF0” và cụm 4 bit thấp được chuyển vào vị trí 4 bit cao bằng lệnh “<< 4”.

Giao diện ghép nối LCD với 4 bit dữ liệu

Hình 4‑4. Giao diện ghép nối LCD với 4 bit dữ liệu.

Chương trình 43: Hiển thị một thông báo trên màn hình LCD bằng chế độ dữ liệu 4 bit
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

/* LCD_4bit.c: Initialize and display “hello” on the LCD using 4-bit data mode.

* All interface uses Port E. Bit 23-20 are used for data.

* Bit 18, 17, 16 are used for control signals EN, R/W, RS respectively

* This program does not poll the status of the LCD.

* It uses delay to wait out the time LCD controller is busy.

* Timing is more relax than the HD44780 datasheet to accommodate the

* variations of the devices.

* You may want to adjust the amount of delay for your LCD controller.

*/

#include <MKL46Z4.H>

#define RS 1  / * BIT0 mask */

#define RW 2  / * BIT1 mask */

#define EN 4  /* BIT2 mask */

void delayMs(int n);

void delayUs(int n);

void LCD_nibble_write(unsigned char data, unsigned char control);

void LCD_command(unsigned char command);

void LCD_data(unsigned char data);

void LCD_init(void);

int main(void)

{

LCD_init();

for(;;)

{

LCD_command(1); /* clear display */

delayMs(500);

LCD_command(0x85); /* set cursor at first line */

LCD_data(‘h’); /* write the word */

LCD_data(‘e’);

LCD_data(‘l’);

LCD_data(‘l’);

LCD_data(‘o’);

delayMs(500);

}

}

void LCD_init(void)

{

SIM->SCGC5 |= (1u << 13); /* enable clock to Port E */

PORTE->PCR[0] = 0x100; /* make PTE0 pin as GPIO */

PORTE->PCR[1] = 0x100; /* make PTE1 pin as GPIO */

PORTE->PCR[2] = 0x100; /* make PTE2 pin as GPIO */

PORTE->PCR[4] = 0x100; /* make PTE4 pin as GPIO */

PORTE->PCR[5] = 0x100; /* make PTE5 pin as GPIO */

PORTE->PCR[6] = 0x100; /* make PTE6 pin as GPIO */

PORTE->PCR[7] = 0x100; /* make PTE7 pin as GPIO */

PTE->PDDR |= (0xF7 << 16); /* make PTE7-4, 2, 1, 0 as output pins */

delayMs(30); /* initialization sequence */

LCD_nibble_write(0x30, 0);

delayMs(10);

LCD_nibble_write(0x30, 0);

delayMs(1);

LCD_nibble_write(0x30, 0);

delayMs(1);

LCD_nibble_write(0x20, 0); /* use 4-bit data mode */

delayMs(1);

LCD_command(0x28); /* set 4-bit data, 2-line, 5×7 font */

LCD_command(0x06); /* move cursor right */

LCD_command(0x01); /* clear screen, move cursor to home */

LCD_command(0x0F); /* turn on display, cursor blinking */

}

void LCD_nibble_write(unsigned char data, unsigned char control)

{

data &= 0xF0; /* clear lower nibble for control */

control &= 0x0F; /* clear upper nibble for data */

PTE->PDOR = ((data | control) << 16); /* RS = 0, R/W = 0 */

PTE->PDOR = ((data | control | EN) << 16); /* pulse E */

delayMs(0);

PTE->PDOR = (data << 16);

PTE->PDOR = 0;

}

void LCD_command(unsigned char command)

{

LCD_nibble_write(command & 0xF0, 0); /* upper nibble first */

LCD_nibble_write(command << 4, 0); /* then lower nibble */

if (command < 4)

delayMs(4); /* commands 1 and 2 need up to 1.64ms */

else

delayMs(1); /* all others 40 us */

}

void LCD_data(unsigned char data)

{

LCD_nibble_write(data & 0xF0, RS); /* upper nibble first */

LCD_nibble_write(data << 4, RS); /* then lower nibble */

delayMs(1);

}

/* Delay n milliseconds

* The CPU core clock is set to MCGFLLCLK at 41.94 MHz in SystemInit().

*/

void delayMs(int n) {

int i;

int j;

for(i = 0 ; i < n; i++)

for(j = 0 ; j < 7000; j++) { }

}

2.4.     Vị trí con trỏ trên LCD

Trong màn hình LCD, chúng ta có thể di chuyển con trỏ đến bất kỳ vị trí nào trong màn hình bằng cách gửi một lệnh chứa địa chỉ của vị trí cần chuyển đến. Ký tự được gửi tiếp theo sẽ xuất hiện ở vị trí con trỏ. Đối với màn hình LCD hai dòng, lệnh địa chỉ cho vị trí đầu tiên của dòng 1 là 0x80 và đối với dòng 2 là 0xC0. Như vậy để di chuyển con trỏ tới vị trí thứ 4 của dòng 1 chứng ta dùng lệnh sau:

LCD_command(0x83);

Hoặc để di chuyển con trỏ tới vị trí thứ 6 của dòng 2 chúng ta dùng lệnh:

LCD_command(0xC5);

Nguyễn Kiêm Hùng

Hung K. Nguyen studied “Electronic Engineering” in both his bachelor’s and master’s degrees at the Vietnam National University, Hanoi, Vietnam. He received the bachelor’s degree in 2003. After receiving his bachelor’s degree, He worked as an internship in the Research Center of Electronics and Telecommunications. In 2006, He received the master’s degree in electronic engineering from VNU University of Engineering and Technology (VNU-UET). Before pursuing his Ph.D’s degree, He worked as a researcher at the Laboratory for Smart Integrated Systems in VNU University of Engineering and Technology for two years. In 2008, He went to Southeast University, Nanjing, China to get his Ph.D degree. He received the Ph.D. degree in Microelectronics and Solid State Electronics from Southeast University in 2013. After got his Ph.D’s degree, He returned to VNU University of Engineering and Technology to continue his research in VLSI design. He works currently as an assistant professor and senior researcher at VNU Key Laboratory for Smart Integrated Systems. His research interests mainly include multimedia processing, reconfigurable computing, and SoC designs.

Trả lời