[LCD và GUI] Bài 2: Tìm hiểu màn hình LCD TFT 1.8 inch ST7735 ( phần 1)

Trong bài này chúng ta sẽ làm việc với màn hình LCD TFT 1.8 inch ST7735, độ phân giải 160×128 sử dụng driver ST7735 và giao tiếp qua đường SPI, trên LCD còn có soket thẻ nhớ ra chân SPI

Các bạn tải datasheet tại đây

Các chân kết nối:

  • 1 : LED : chân điều khiển led nền, led nền sẽ sáng khi cấp điện 3v vô chân này
  • 2: Chân SCK , chân clk của giao tiếp SPI
  • 3: Chân SDA, chân data của giao tiếp SPI
  • 4: Chân A0, chân này thể hiển việc bạn đang truyền dữ liệu màu sắc hiển thị hay là truyền lệnh điều khiển hoạt động của màn hình. Khi chân A0 bằng 0 thì LCD sẽ hiểu là dữ liệu bản gửi đến là các lệnh để cấu hình, điều khiển LCD. Còn khi A0 bằng 1 thì dữ liệu bạn gửi sẽ được hiểu là dữ liệu hiển thị lên các pixel trên màn hình
  • 5: Chân reset LCD, bình thường nó nối lên mức 1, lúc cần reset thì kéo xuống 0
  • 6: Chân CS của giao tiếp SPI
  • 7:GND
  • 8: VCC 3V đến 5V

Nguyên tắc điều khiển chung

Khi khởi động chúng ta sẽ gửi số lệnh để cấu hình các chế độ hoạt động của màn LCD ví dụ số màu, độ phân giải của màn, kiểu truyền dữ liệu, độ tương phản ….

Sau khi khởi động xong, chúng ta gửi dữ liệu cần hiển thị ra màn hình theo các mà chúng ta đã cấu hình màn hình

ST7735 hỗ trợ các chế độ mà 12bit (444) 16 bit(565) và 18 bit (666). Trong bài này, mình sẽ hướng dẫn các bạn ở chế độ màu 16bit ( RGB565)

Kết nối phần cứng

2 chân LED và RESET các bạn có thể nối thẳng lên VCC, hoặc nối vào vđk nếu muốn điều khiển chúng. Ở đây mình sẽ cho nối vào vi điều khiển

STM32LCD
B12 LED
SCK SPI2 ( B13)SCK
MOSI SPI2 (B15)SDA
A8A0
A9RESET
A10CS
GNDGND
VCC3V hoặc 5V

Cấu hình cubeMx

Thạch anh ngoại 8M, CPU clock 72Mhz, bật SPI2 chế độ Tranmis Only do chúng ta chỉ cần gửi dữ liệu đi chứ không cần đọc về và bật DMA TX. Bật Sys Debug để debug chương trình. Cấu hình các chân GPIO tương ứng

st7735
st7735
st7735

Tạo code và chúng ta bắt đầu tạo thư viện LCD

Các bạn vào file -> new và tạo ra 2 file st7735.cst7735.h và lưu nó tại ví trí thư mục src và inc ( cùng cấp với file main đó)

Trong cây project ở KeilC các bạn click đúp vào Application/User/Core để add file st7735.c vào project

Vậy là xong rồi đó, chúng ta bắt đầu viết nội dung cho thư viện st7735. Các bạn copy và dán template mô hình thư viện mẫu vào các file st7735.c st7735.h

File .c chưa nội dung của code

File .h để khai báo nguyên mẫu hàm

Do chúng ta sử dụng giao tiếp SPI nên mình sẽ tạo 1 hàm ST7735_init và truyền vào con trỏ chưa bộ SPI mà CUBEMX sinh ra vào thư viện và tạo 1 con trỏ SPI toàn cục để lưu trữ nó

Rồi, bây giờ mình đã có bộ spi, mình sẽ xây dựng 1 hàm để gửi 1 byte đi qua SPI. Thực ra HAL đã hỗ trợ sẵn hàm HAL_SPI_Transmit nhưng mình sẽ không sử dụng nó vì cái hàm này chạy rất chậm, đã đến lúc can thiệp trực tiếp vào thanh ghi để ra lệnh gửi nhằm tối ưu hóa tốc độ của chương trình

Để gửi 1 byte đi, chúng ta sẽ kiểm tra cờ SR_TXE trong thanh ghi SR xem nó có bằng 1 không. Nếu bằng 1 thì tức là sẵn sàng để gửi data đi. Ngược lại thì chờ cho nó lên 1 rồi mới gửi. Để gửi thì chỉ việc ghi data vào thanh ghi DR

Hàm truyền 1byte sẽ như sau

Hàm truyện 1 lệnh

Hàm truyền lệnh được hiểu là truyền data trong khi chân A0 đang ở mức 0. Mình sẽ #define lệnh bật và tắt chân A0 và chân CS vào file .h Để cho nhanh, thay vì dùng hàm HAL_GPIO_WritePin thì mình sẽ truy cập trực tiếp vào thanh ghi BSRR

Trước khi đưa chân A0 xuống thấp mình sẽ kiểm tra để đảm bảo đường SPI rảnh rỗi ( tránh phá hỏng bus nếu có dữ liệu đang truyền trước đó) và sau khi truyền mình muốn đưa chân A0 lên 1 để đảm bảo trạng thái bình thường chân A0 luôn là 1 ( làm thế để khi gửi data hiển thị lên màn hình chúng ta không cần set chân A0 lên 1 nữa vì mặc định nó là ở múc 1 sẵn rồi (sẽ tiết kiệm 1 vài chu kì máy :D)). Đương nhiên trước khi đưa A0 lên 1 thì m cũng phải kiểm tra đã truyền xong để tránh làm hỏng đường truyền

Bây giờ mình sẽ dùng hàm ST7735_sendCommand để gửi các lệnh cấu hình cho module LCD trong hàm ST7735_init

Quy trình gửi lệnh

Đầu tiên gửi mã lệnh ( địa chỉ thanh ghi cần cấu hình ), sau đó gửi lần lượt các giá trị cấu hình. LCD sẽ tự kết thúc lệnh khi nó đã nhận đủ các byte cấu hình cho lệnh đó

Các bạn mở datasheet ST7735 trang 78 để xem bảng thống kê và chức năng của các lệnh

Dưới đây là toàn bộ hàm khởi tạo LCD. Mình đã ghi chú các lệnh, còn giá trị cấu hình thì các bạn có thể nghiên cứu kĩ trong datasheet

Một số command quan trọng mà bạn cần quan tâm nên mình sẽ giải thích

Lệnh cài chế độ màu (0x3A)

Với 0x05 là 16bit màu, 0x03 là 12 bit màu, 0x06 là 18 bit màu. Ở đây ta dùng 16bit nên cài là 0x05

Lệnh cài chế độ màu (0x36) và xoay màn hình ngang dọc

Nếu là 0xZ0 thì là RGB mode, còn 0xZ8 BGR ( chú ý Z là 4 bit dùng để cài chế độ xoay của màn hình)

Các bạn tham khảo chi tiết command 0x36 trong datasheet để biết cách cài các kiểu xoay màn hình. Ở đây mình cài là 0xA0 thì nó sẽ xoay ngang màn hinh

Đảo ngược hiển thị

Gửi lệnh 0x20 để tắt đảo ngược và 0x21 để bật đảo ngược

Lệnh chọn vị trí của con trỏ

Gửi lệnh 0x2C để dịch con trỏ về tọa độ 0,0 của vùng giới hạn con trỏ

Lệnh chọn vùng giới hạn con trỏ

Đây là 1 lệnh rất quan trọng nên mình sẽ giải thích kĩ tuy nhiên trước khi giải thích lệnh này, chúng ta sẽ test qua chương trình đã viết từ nãy tới giờ nhé

Các bạn thêm tất cả nguyên mẫu hàm vào file st7735.h và include thư viện st7735.h vào main.c để có thể gọi được các hàm của thư viện trong main.c

Trong hàm main mình sẽ bật 2 chân RESET và LED nền rồi gọi hàm ST7735_init() với tham số là bộ SPI2

Nạp thử chương trình và test thử, lúc này màn hình sẽ hiện loạn xị ngậu

tft 1.8 inch

Bây giờ mình sẽ thử vẽ lên vài pixel màu đen bằng cách gửi SPI_WriteByte(0); và trước đó, mình phải gửi lệnh ST7735_sendCommand(0x2C); để chọn tọa độ con trỏ về 0,0

tft 1.8 inch

Như vậy mình đã ghi thành công 10000 điểm ảnh màu đen vào LCD

Nguyên tắc ghi dữ liệu như sau:

Do chúng ta thiết lập chế độ 16bit / 1 pixel nên để hiển thị 1 pixel ta cần gửi liên tiếp 2 byte màu. Chế độ màu của chúng ta là RGB 565 nên 5 bit cao nhất sẽ đại diện cho màu Đỏ 6bit tiếp theo đại diện cho màu Xanh Lá và 5 bit thấp nhất đại diện cho màu Xanh Dương

Rgb888 to rgb565 converter. RGB565 to RGB888 Color Conversion

Ví dụ 0xF800 tức 0b11110000000000 sẽ tương đương với pixel màu đỏ

Thử gửi lại với màu đỏ nhé

tft 1.8 inch

Đó là cách chúng ta gửi 1 pixel lên màn hình, và cứ mỗi 1 pixel được gửi thành công, con trỏ vị trí của màn hình sẽ tự động tăng lên 1 để lần gửi tiếp theo nó sẽ in ra pixel bên cạnh chứ không ghi đè lên pixel cũ nữa. Và khi con trỏ chạm tới giới hạn của vùng giới hạn con trỏ, nó sẽ tự động xuống dòng và lùi về đầu dòng. Khi con trỏ chạm tới pixel cuối cùng của dòng cuối, nó sẽ quay trở lại pixel đầu tiên

Hãy nhìn vào hình sau:

Vùng màu đen là bộ nhớ chứa dữ liệu của màn hình ic driver ST7735, nó được thiết kế với kích thước là 162×132 pixel, do đó nó sẽ lớn hơn kích thước 160×128 của màn hình LCD ta đang xài. Như vậy, bộ tăng con trỏ tự động phải đạt tới giá trị 162 nó mới xuống dòng, nhưng màn hình ta đang xài chỉ có độ phân giải tối đa là 128, do vậy chúng ta có thể thiết lập vùng giới hạn của con trỏ ở ngưỡng 128 để nó tự động xuống dòng khi đạt giá trị 128.

Tương tự, ta thiết lập chiều dọc ở ngưỡng 132 để nó quay trở lại pixel đầu khi con trỏ tăng tới 128

Mẹo: Bạn sẽ không thể vẽ lên pixel bên ngoài của vùng giới hạn con trỏ được. Do đó có thể lợi dụng điều này giới hạn 1 vùng nhất định mà chúng ta muốn vẽ

Để cài đặt vùng giới hạn con trỏ, ta sẽ ghi vào 2 thanh ghi có địa chỉ 0x2A và 0x2B. Trong đó thanh ghi 0x2A lưu tọa độ giới hạn chiều ngang ( trục X) còn thanh ghi 0x2B lưu tọa độ giới hạn chiều dọc (truc Y)

Ví dụ

Để thuận tiện cho việc chọn vùng hoạt động của con trỏ, mình sẽ tạo cho nó 1 hàm riêng

Bây giờ, do lúc khởi động màn hình hiện linh tinh nên mình sẽ viết 1 hàm để clear màn bình về màu đen

Gọi hàm ST7735_Clr() cuối hàm ST7735_init() nhé !
Thêm nguyên mẫu hàm của các hàm mới vào file .h và quay lại hàm main, xóa các đoạn code test đi, chỉ gọi mỗi hàm ST7735_init(&hspi2); thôi

Chạy thử và ta đã hoàn thành việc khởi tạo màn hình LCD 1.8 inch. Mặc định sau khi khởi tạo nó sẽ sáng hết màu đen

Hiển thị thử 1 hình ảnh lên

Mình sẽ demo hiển hình ảnh kích cỡ 160×128 lên nhé, ví dụ với 1 hình ảnh đầy tính nhân văn này

À mà mình đang để LCD nằm ngang nên mình sẽ quay ảnh lại

Vào trang web này để conver ảnh sang dangj byte HEX https://lvgl.io/tools/imageconverter

Chọn ảnh và chọn C-array rồi convert

Sau khi conver nó sẽ cho ta 1 file với 1 đống định dạng màu, các bạn Ctrl+F để tìm kiếm tới định dạng /Pixel format: Blue: 5 bit, Green: 6 bit, Red: 5 bit BUT the 2 bytes are swapped/

Copy đoạn mã hex đó ra và cho vào mảng dữ liệu

Ta viết thêm 1 hàm in hình ảnh ra, mình sẽ viết hàm này ở hàm main.c

Đương nhiên là không quên gọi hàm sau ra

Và đây là kết quả

Kết luận

Như vậy, trong bài này, mình đã giúp các bạn bước đầu làm quen với màn hình đồ họa Graphic và đã viết được các hàm driver cơ sở để giao tiếp với màn hình. Tuy nhiên đây mới chỉ là bắt đầu seris LCD và GUI có 1 không 2 của mình. Trong bài tiếp theo chúng ta sẽ tiếp tục thiết kế thư viện đồ họa cơ bản

Download

Phần source cho bài này các bạn có thể tải tại đây

Mất rất nhiều thời gian cho mỗi bài viết vì không chỉ đơn giản là đánh máy mà còn phải bỏ nhiều thời gian để nghiên cứu datasheet, viết code, test thử. Vì vậy các bạn có thể ủng hộ các bài viết của mình bằng cách nhấp vào quảng cáo. Mình sẽ cố gắng ra bài đều đặn và chất lượng hơn. Xin cảm ơn !

 

Từ tác giả:

Nếu có bất kì thắc mắc nào trong bài viết, vui lòng để lại comment dưới mỗi bài ! Mình sẽ không trả lời thắc mắc của các bạn ở facebook hay email !

Nếu trong phần code bạn nhìn thấy nhưng thứ kiểu như &amp; thì đó là lỗi hiển thị, cụ thể 3 kí tự < > & bị biến đổi thành như thế
&amp; là &
&lt;  là <
&gt; là >

Giới thiệu Đào Nguyện 80 bài viết
DIY,chế cháo, viết blog chia sẽ kiến thức về lập trình,điện tử - IoT. Rất mong được giao lưu, kết bạn với các bạn cùng đam mê. Địa chỉ Facebook: https://www.facebook.com/nguyendao207

2 bình luận

Đã đóng bình luận.