基于STM32驱动NT35510电容屏LCD的程序,采用FSMC 16位并行接口,支持电容触摸。
一、NT35510液晶屏基础信息
1、屏幕参数
- 分辨率:480×854(竖屏)或 854×480(横屏)
- 接口:16位8080并行总线
- 控制器:NT35510 + GT911电容触摸
- 显存:内置,不需要外部SDRAM
2、硬件连接(STM32F407)
| NT35510引脚 | STM32引脚 | 功能 |
|---|---|---|
| DB0-DB15 | PD0-PD15 | 16位数据线 |
| RD | PD4 | 读使能 |
| WR | PD5 | 写使能 |
| RS/DC | PD11 | 数据/命令选择 |
| CS | PD7 | 片选 |
| RST | PG9 | 复位 |
| BLK | PB0 | 背光 |
二、FSMC配置(STM32CubeMX)
1、FSMC配置参数
Bank: Bank1
Memory Type: LCD Interface
Data Width: 16 bits
Address: 0x6C000000
2、时序配置
FSMC_NORSRAM_TimingTypeDef Timing = {0};
Timing.AddressSetupTime = 1;
Timing.AddressHoldTime = 0;
Timing.DataSetupTime = 8;
Timing.BusTurnAroundDuration = 0;
Timing.CLKDivision = 0;
Timing.DataLatency = 0;
Timing.AccessMode = FSMC_ACCESS_MODE_A;
三、驱动文件结构
NT35510/
├── nt35510.h // 驱动头文件
├── nt35510.c // 驱动实现
├── font.h // 字库
├── touch.h // 触摸驱动
└── lcd_conf.h // 配置参数
四、驱动代码
1、头文件定义
// nt35510.h
#ifndef __NT35510_H
#define __NT35510_H
#include "stm32f4xx_hal.h"
// LCD尺寸
#define LCD_WIDTH 480
#define LCD_HEIGHT 854
// 显存地址(16位颜色)
#define LCD_FRAME_BUFFER ((uint16_t*)0xC0000000)
// 颜色定义(RGB565)
#define COLOR_BLACK 0x0000
#define COLOR_NAVY 0x000F
#define COLOR_DARKGREEN 0x03E0
#define COLOR_DARKCYAN 0x03EF
#define COLOR_MAROON 0x7800
#define COLOR_PURPLE 0x780F
#define COLOR_OLIVE 0x7BE0
#define COLOR_LIGHTGREY 0xC618
#define COLOR_DARKGREY 0x7BEF
#define COLOR_BLUE 0x001F
#define COLOR_GREEN 0x07E0
#define COLOR_CYAN 0x07FF
#define COLOR_RED 0xF800
#define COLOR_MAGENTA 0xF81F
#define COLOR_YELLOW 0xFFE0
#define COLOR_WHITE 0xFFFF
#define COLOR_ORANGE 0xFD20
#define COLOR_GREENYELLOW 0xAFE5
#define COLOR_PINK 0xF81F
// 方向定义
typedef enum {
LCD_DIRECTION_0 = 0, // 竖屏
LCD_DIRECTION_90 = 1, // 横屏
LCD_DIRECTION_180 = 2, // 竖屏旋转180
LCD_DIRECTION_270 = 3 // 横屏旋转180
} LCD_Direction_t;
// 触摸点结构
typedef struct {
uint16_t x;
uint16_t y;
uint8_t pressed;
} TouchPoint_t;
// 函数声明
void NT35510_Init(void);
void NT35510_SetDirection(LCD_Direction_t dir);
void NT35510_Clear(uint16_t color);
void NT35510_DrawPixel(uint16_t x, uint16_t y, uint16_t color);
void NT35510_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void NT35510_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void NT35510_FillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void NT35510_DrawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
void NT35510_FillCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
void NT35510_ShowChar(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bgcolor);
void NT35510_ShowString(uint16_t x, uint16_t y, char *str, uint16_t color, uint16_t bgcolor);
void NT35510_ShowImage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *img);
void NT35510_SetWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void NT35510_WriteData(uint16_t data);
uint16_t NT35510_ReadData(void);
// 触摸相关
void NT35510_Touch_Init(void);
TouchPoint_t NT35510_Touch_Read(void);
uint8_t NT35510_Touch_Pressed(void);
#endif
2、核心寄存器操作
// nt35510.c
#include "nt35510.h"
#include "font.h"
#include <string.h>
// FSMC地址定义
#define NT35510_CMD_ADDR ((uint16_t*)0x6C000000) // RS=0
#define NT35510_DATA_ADDR ((uint16_t*)0x6C000800) // RS=1 (A10=1)
// 私有全局变量
static LCD_Direction_t lcd_direction = LCD_DIRECTION_0;
static uint16_t lcd_width = LCD_WIDTH;
static uint16_t lcd_height = LCD_HEIGHT;
// 写命令
static void NT35510_WriteCmd(uint16_t cmd) {
*NT35510_CMD_ADDR = cmd;
}
// 写数据
static void NT35510_WriteData(uint16_t data) {
*NT35510_DATA_ADDR = data;
}
// 读数据
static uint16_t NT35510_ReadData(void) {
return *NT35510_DATA_ADDR;
}
// 设置窗口
void NT35510_SetWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
NT35510_WriteCmd(0x2A); // 列地址设置
NT35510_WriteData(x1 >> 8);
NT35510_WriteData(x1 & 0xFF);
NT35510_WriteData(x2 >> 8);
NT35510_WriteData(x2 & 0xFF);
NT35510_WriteCmd(0x2B); // 行地址设置
NT35510_WriteData(y1 >> 8);
NT35510_WriteData(y1 & 0xFF);
NT35510_WriteData(y2 >> 8);
NT35510_WriteData(y2 & 0xFF);
NT35510_WriteCmd(0x2C); // 写GRAM
}
3、NT35510初始化序列
// NT35510初始化
void NT35510_Init(void) {
// 1. 硬件复位
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_9, GPIO_PIN_RESET); // RST低
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_9, GPIO_PIN_SET); // RST高
HAL_Delay(50);
// 2. 背光开启
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
// 3. 发送初始化序列
NT35510_WriteCmd(0xF000);
NT35510_WriteData(0x55);
NT35510_WriteCmd(0xF001);
NT35510_WriteData(0xAA);
NT35510_WriteCmd(0xF002);
NT35510_WriteData(0x52);
NT35510_WriteCmd(0xF003);
NT35510_WriteData(0x08);
NT35510_WriteCmd(0xF004);
NT35510_WriteData(0x00);
// Display Control
NT35510_WriteCmd(0xB000);
NT35510_WriteData(0x0D);
NT35510_WriteData(0x00);
NT35510_WriteData(0x14);
NT35510_WriteData(0x0C);
// Power Control
NT35510_WriteCmd(0xB600);
NT35510_WriteData(0x34);
NT35510_WriteData(0x34);
NT35510_WriteData(0x34);
NT35510_WriteData(0x34);
// Driver Output Control
NT35510_WriteCmd(0xB800);
NT35510_WriteData(0x0F);
NT35510_WriteData(0x0F);
// LCD Driving Control
NT35510_WriteCmd(0xBA00);
NT35510_WriteData(0x0F);
// Frame Rate Control
NT35510_WriteCmd(0xB900);
NT35510_WriteData(0x24);
// Gamma Control
NT35510_WriteCmd(0xBE00);
NT35510_WriteData(0x00);
NT35510_WriteData(0x04);
// Set Pixel Format
NT35510_WriteCmd(0x3A00);
NT35510_WriteData(0x55); // 16位RGB565
// Sleep Out
NT35510_WriteCmd(0x1100);
HAL_Delay(120);
// Display On
NT35510_WriteCmd(0x2900);
HAL_Delay(50);
// 4. 设置方向
NT35510_SetDirection(LCD_DIRECTION_0);
// 5. 清屏
NT35510_Clear(COLOR_BLACK);
}
4、方向控制
// 设置显示方向
void NT35510_SetDirection(LCD_Direction_t dir) {
lcd_direction = dir;
NT35510_WriteCmd(0x3600); // MADCTL命令
switch(dir) {
case LCD_DIRECTION_0: // 竖屏
NT35510_WriteData(0x00);
lcd_width = 480;
lcd_height = 854;
break;
case LCD_DIRECTION_90: // 横屏
NT35510_WriteData(0x60);
lcd_width = 854;
lcd_height = 480;
break;
case LCD_DIRECTION_180: // 竖屏旋转180
NT35510_WriteData(0xC0);
lcd_width = 480;
lcd_height = 854;
break;
case LCD_DIRECTION_270: // 横屏旋转180
NT35510_WriteData(0xA0);
lcd_width = 854;
lcd_height = 480;
break;
}
}
5、基本绘图函数
// 清屏
void NT35510_Clear(uint16_t color) {
uint32_t i;
uint32_t total = lcd_width * lcd_height;
NT35510_SetWindow(0, 0, lcd_width-1, lcd_height-1);
for(i = 0; i < total; i++) {
NT35510_WriteData(color);
}
}
// 画点
void NT35510_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
if(x >= lcd_width || y >= lcd_height) return;
NT35510_SetWindow(x, y, x, y);
NT35510_WriteData(color);
}
// 画线
void NT35510_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while(1) {
NT35510_DrawPixel(x1, y1, color);
if(x1 == x2 && y1 == y2) break;
int e2 = err * 2;
if(e2 > -dy) {
err -= dy;
x1 += sx;
}
if(e2 < dx) {
err += dx;
y1 += sy;
}
}
}
// 画矩形
void NT35510_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
NT35510_DrawLine(x1, y1, x2, y1, color);
NT35510_DrawLine(x2, y1, x2, y2, color);
NT35510_DrawLine(x2, y2, x1, y2, color);
NT35510_DrawLine(x1, y2, x1, y1, color);
}
// 填充矩形
void NT35510_FillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
uint16_t i, j;
if(x1 > x2) { uint16_t tmp = x1; x1 = x2; x2 = tmp; }
if(y1 > y2) { uint16_t tmp = y1; y1 = y2; y2 = tmp; }
for(i = y1; i <= y2; i++) {
for(j = x1; j <= x2; j++) {
NT35510_DrawPixel(j, i, color);
}
}
}
// 画圆
void NT35510_DrawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) {
int x = 0;
int y = r;
int d = 3 - 2 * r;
while(x <= y) {
NT35510_DrawPixel(x0 + x, y0 + y, color);
NT35510_DrawPixel(x0 + y, y0 + x, color);
NT35510_DrawPixel(x0 - y, y0 + x, color);
NT35510_DrawPixel(x0 - x, y0 + y, color);
NT35510_DrawPixel(x0 - x, y0 - y, color);
NT35510_DrawPixel(x0 - y, y0 - x, color);
NT35510_DrawPixel(x0 + y, y0 - x, color);
NT35510_DrawPixel(x0 + x, y0 - y, color);
if(d < 0) {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y--;
}
x++;
}
}
// 填充圆
void NT35510_FillCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) {
int x = 0;
int y = r;
int d = 3 - 2 * r;
while(x <= y) {
// 画水平线填充
NT35510_DrawLine(x0 - x, y0 + y, x0 + x, y0 + y, color);
NT35510_DrawLine(x0 - y, y0 + x, x0 + y, y0 + x, color);
NT35510_DrawLine(x0 - y, y0 - x, x0 + y, y0 - x, color);
NT35510_DrawLine(x0 - x, y0 - y, x0 + x, y0 - y, color);
if(d < 0) {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y--;
}
x++;
}
}
6、字符显示函数
// 显示一个字符
void NT35510_ShowChar(uint16_t x, uint16_t y, char ch, uint16_t color, uint16_t bgcolor) {
uint8_t i, j;
uint8_t temp;
if(x > lcd_width - 8 || y > lcd_height - 16) return;
// 获取字符点阵
uint8_t *font = (uint8_t*)&ASCII_8x16[((uint8_t)ch - ' ') * 16];
for(i = 0; i < 16; i++) {
temp = font[i];
for(j = 0; j < 8; j++) {
if(temp & 0x80) {
NT35510_DrawPixel(x + j, y + i, color);
} else if(bgcolor != color) {
NT35510_DrawPixel(x + j, y + i, bgcolor);
}
temp <<= 1;
}
}
}
// 显示字符串
void NT35510_ShowString(uint16_t x, uint16_t y, char *str, uint16_t color, uint16_t bgcolor) {
while(*str) {
NT35510_ShowChar(x, y, *str++, color, bgcolor);
x += 8;
if(x > lcd_width - 8) {
x = 0;
y += 16;
}
}
}
7、图片显示(BMP格式)
// 显示图片(BMP格式,16位色)
void NT35510_ShowImage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *img) {
uint32_t i, j;
uint16_t color;
const uint16_t *pixel = (const uint16_t*)img;
NT35510_SetWindow(x, y, x + width - 1, y + height - 1);
for(i = 0; i < height; i++) {
for(j = 0; j < width; j++) {
color = *pixel++;
NT35510_WriteData(color);
}
}
}
五、电容触摸驱动(GT911)
1、GT911触摸驱动
// touch.h
#ifndef __TOUCH_H
#define __TOUCH_H
#include "stm32f4xx_hal.h"
#define GT911_I2C_ADDR 0xBA // 7位地址
#define GT911_MAX_TOUCH 5
// 触摸点结构
typedef struct {
uint8_t track_id; // 跟踪ID
uint16_t x; // X坐标
uint16_t y; // Y坐标
uint16_t size; // 触摸面积
} TouchPoint_t;
// 触摸状态
typedef struct {
uint8_t status; // 状态
uint8_t touch_count; // 触摸点数
TouchPoint_t points[GT911_MAX_TOUCH]; // 触摸点数组
} TouchData_t;
// 函数声明
void GT911_Init(void);
uint8_t GT911_Read(TouchData_t *touch_data);
void GT911_ClearStatus(void);
uint8_t GT911_Check(void);
#endif
// touch.c
#include "touch.h"
#include "nt35510.h"
extern I2C_HandleTypeDef hi2c1; // 假设使用I2C1
// GT911寄存器
#define GT911_PRODUCT_ID 0x8140
#define GT911_STATUS_REG 0x814E
#define GT911_DATA_REG 0x814F
#define GT911_COMMAND_REG 0x8040
// 初始化GT911
void GT911_Init(void) {
uint8_t config_data[] = {
0x5A, 0x5A, 0x04, 0x80, 0x07, 0x0A, 0x3D, 0x20, 0x03, 0x08,
0x28, 0x0F, 0x50, 0x32, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x1A, 0x1E, 0x14, 0x8C, 0x2E, 0x0F,
0x2A, 0x29, 0xAF, 0x41, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};
// 软复位
uint8_t reset_cmd = 0x02;
HAL_I2C_Mem_Write(&hi2c1, GT911_I2C_ADDR, GT911_COMMAND_REG,
I2C_MEMADD_SIZE_8BIT, &reset_cmd, 1, 100);
HAL_Delay(10);
// 写入配置
HAL_I2C_Mem_Write(&hi2c1, GT911_I2C_ADDR, 0x8047,
I2C_MEMADD_SIZE_8BIT, config_data, sizeof(config_data), 100);
// 计算校验和
uint8_t checksum = 0;
for(int i = 0; i < sizeof(config_data); i++) {
checksum += config_data[i];
}
checksum = (~checksum) + 1;
HAL_I2C_Mem_Write(&hi2c1, GT911_I2C_ADDR, 0x80FF,
I2C_MEMADD_SIZE_8BIT, &checksum, 1, 100);
// 保存配置
uint8_t save_cmd = 0x00;
HAL_I2C_Mem_Write(&hi2c1, GT911_I2C_ADDR, GT911_COMMAND_REG,
I2C_MEMADD_SIZE_8BIT, &save_cmd, 1, 100);
HAL_Delay(10);
}
// 读取触摸数据
uint8_t GT911_Read(TouchData_t *touch_data) {
uint8_t status = 0;
uint8_t buffer[1 + 8 * GT911_MAX_TOUCH] = {0};
// 读取状态寄存器
if(HAL_I2C_Mem_Read(&hi2c1, GT911_I2C_ADDR, GT911_STATUS_REG,
I2C_MEMADD_SIZE_8BIT, &status, 1, 10) != HAL_OK) {
return 0;
}
// 检查是否有触摸
if((status & 0x80) == 0) {
return 0;
}
// 读取触摸点数
touch_data->touch_count = status & 0x0F;
if(touch_data->touch_count > GT911_MAX_TOUCH) {
touch_data->touch_count = GT911_MAX_TOUCH;
}
// 读取触摸数据
if(HAL_I2C_Mem_Read(&hi2c1, GT911_I2C_ADDR, GT911_DATA_REG,
I2C_MEMADD_SIZE_8BIT, buffer, 1 + 8 * touch_data->touch_count, 10) != HAL_OK) {
return 0;
}
// 解析触摸点
for(int i = 0; i < touch_data->touch_count; i++) {
uint8_t *p = &buffer[1 + i * 8];
touch_data->points[i].track_id = p[0];
touch_data->points[i].x = ((uint16_t)p[2] << 8) | p[1];
touch_data->points[i].y = ((uint16_t)p[4] << 8) | p[3];
touch_data->points[i].size = ((uint16_t)p[6] << 8) | p[5];
// 坐标转换(根据LCD方向)
if(lcd_direction == LCD_DIRECTION_0) {
// 竖屏
touch_data->points[i].x = touch_data->points[i].x * lcd_width / 854;
touch_data->points[i].y = touch_data->points[i].y * lcd_height / 480;
} else {
// 横屏
touch_data->points[i].x = touch_data->points[i].x * lcd_width / 480;
touch_data->points[i].y = touch_data->points[i].y * lcd_height / 854;
}
}
// 清除状态
GT911_ClearStatus();
return touch_data->touch_count;
}
// 清除触摸状态
void GT911_ClearStatus(void) {
uint8_t clear_cmd = 0x00;
HAL_I2C_Mem_Write(&hi2c1, GT911_I2C_ADDR, GT911_STATUS_REG,
I2C_MEMADD_SIZE_8BIT, &clear_cmd, 1, 10);
}
六、主程序示例
// main.c
#include "main.h"
#include "nt35510.h"
#include "touch.h"
int main(void) {
HAL_Init();
SystemClock_Config();
// 1. 初始化外设
MX_GPIO_Init();
MX_FSMC_Init();
MX_I2C1_Init(); // 触摸屏I2C
// 2. 初始化LCD
NT35510_Init();
// 3. 初始化触摸
GT911_Init();
// 4. 显示测试界面
NT35510_Clear(COLOR_BLACK);
NT35510_ShowString(10, 10, "NT35510 LCD Test", COLOR_WHITE, COLOR_BLACK);
NT35510_ShowString(10, 30, "Touch the screen", COLOR_YELLOW, COLOR_BLACK);
// 5. 主循环
TouchData_t touch_data;
char str[32];
while(1) {
// 读取触摸
if(GT911_Read(&touch_data) > 0) {
// 显示触摸点
for(int i = 0; i < touch_data.touch_count; i++) {
TouchPoint_t *p = &touch_data.points[i];
// 画触摸点
NT35510_FillCircle(p->x, p->y, 20, COLOR_RED);
// 显示坐标
sprintf(str, "Touch %d: %d,%d", i+1, p->x, p->y);
NT35510_ShowString(10, 60 + i*20, str, COLOR_CYAN, COLOR_BLACK);
}
// 延时避免频繁刷新
HAL_Delay(50);
// 清除触摸点显示
for(int i = 0; i < touch_data.touch_count; i++) {
TouchPoint_t *p = &touch_data.points[i];
NT35510_FillCircle(p->x, p->y, 20, COLOR_BLACK);
}
}
// 其他任务...
HAL_Delay(10);
}
}
七、DMA加速显示(可选)
// 使用DMA加速矩形填充
void NT35510_FillRectangle_DMA(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
uint32_t width = x2 - x1 + 1;
uint32_t height = y2 - y1 + 1;
uint32_t total = width * height;
// 准备颜色缓冲区
static uint16_t color_buffer[320];
for(int i = 0; i < 320; i++) {
color_buffer[i] = color;
}
// 设置窗口
NT35510_SetWindow(x1, y1, x2, y2);
// 使用DMA传输
for(uint32_t i = 0; i < height; i++) {
// DMA传输一行
HAL_DMA_Start(&hdma_memtomem, (uint32_t)color_buffer,
(uint32_t)NT35510_DATA_ADDR, width);
// 等待传输完成
while(__HAL_DMA_GET_FLAG(&hdma_memtomem, DMA_FLAG_TCIF3_7));
__HAL_DMA_CLEAR_FLAG(&hdma_memtomem, DMA_FLAG_TCIF3_7);
}
}
参考代码 STM32的相应程序用于驱动电容屏LCD_NT35510 www.youwenfan.com/contentcnu/70039.html
八、性能优化
1. 使用DMA双缓冲
uint16_t frame_buffer1[480*100]; // 部分缓冲
uint16_t frame_buffer2[480*100];
2. 使用硬件JPEG解码(STM32F7/H7)
JPEG_Decode_DMA(&hjpeg, jpeg_data, jpeg_size, rgb_buffer);
3. 使用LTDC图层(STM32F429/F7/H7)
// 配置LTDC
hltdc.Init.HorizontalSync = 41;
hltdc.Init.VerticalSync = 10;
hltdc.Init.AccumulatedHBP = 53;
hltdc.Init.AccumulatedVBP = 12;
九、常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| 屏幕不亮 | 背光未开启 | 检查背光控制引脚 |
| 花屏 | 时序不对 | 调整FSMC时序参数 |
| 触摸不准 | 坐标映射错误 | 校准触摸参数 |
| 刷新慢 | 单点写屏 | 使用DMA或设置窗口批量写 |