2.4寸SPI串口ILI9341芯片彩色LCD驱动

该LCD分辨率为240 * 320. 与单片机连接需要接以下引脚

  • GND: 地线
  • 3.3V: 3.3V供电
  • BL:背光信号,可以使用PWM调节背光亮度
  • SCK: SPI时钟信号
  • RST: LCD Reset引脚
  • CS: SPI 片选引脚,如果只接一个SPI设备,该引脚可以直接接地
  • A0:命令/数据选择引脚
  • SDI: SPI数据输入引脚,MOSI
  • SDO: SPI数据输出引脚,MISO,如果不读取液晶屏的数据,这个引脚可以不接

我们采用STM32F103RB单片机驱动该屏幕,连接好GND和3.3v后,其他引脚接线如下:

  1. 将BL引脚接到TIM2_CH2引脚 PA1,在STM32CubeMX中设置TIM2 Channel2为PWM Generation。根据我们希望的亮度值,设置Prescalar与ARR(详情见此文:xxxxx)

  2. 在STM32CubeMX中将SPI2设置为Full Duplex Master(如果不打算读取液晶屏的数据,设成Transmit Only Master也是可以的。)。将PB13脚接到LCD SCK脚,PB15脚接到LCD SDI脚。

  3. 将PB0接到LCD RST脚, PB1接到LCD A0脚,PB2接到LCD CS脚。并在STM32CubeMX中将这三个引脚的模式都设为Output Push Pull (推挽输出)。输出level High,最大输出速度 High。并将三个引脚分别命名为LCD_RST, LCD_A0, LCD_CS

完成以上设置于接线之后,我们就可以生成代码并实现相关驱动的代码了,部分关键代码如下所示:

复制代码
#define LCD_ORIENTATION_LANDSCAPE 0x28
#define LCD_ORIENTATION_PORTRAIT 0x48
#define LCD_BASE_WIDTH 240 
#define LCD_BASE_HEIGHT 320

#define LCD_ORIENTATION LCD_ORIENTATION_PORTRAIT

//默认是竖屏,所以宽度240,高度320,当横屏时,需要交换设置
#define LCD_WIDTH (LCD_ORIENTATION == LCD_ORIENTATION_PORTRAIT ? LCD_BASE_WIDTH : LCD_BASE_HEIGHT)
#define LCD_HEIGHT (LCD_ORIENTATION == LCD_ORIENTATION_PORTRAIT ? LCD_BASE_HEIGHT : LCD_BASE_WIDTH)


#define LCD_MODE_DATA() HAL_GPIO_WritePin(LCD_A0_GPIO_Port, LCD_A0_Pin, GPIO_PIN_SET)
#define LCD_MODE_CMD() HAL_GPIO_WritePin(LCD_A0_GPIO_Port, LCD_A0_Pin, GPIO_PIN_RESET)
#define LCD_CHIP_SELECT() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET)
#define LCD_CHIP_UNSELECT() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET)

// 通过拉低LCD_RST引脚实现LCD硬复位
void LCD_Hard_Reset() {

  HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
  HAL_Delay(20);
  HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
  HAL_Delay(120);

}

// 发送一个字节的命令
void LCD_Send_Cmd(uint8_t cmd) {
  LCD_CHIP_SELECT();
  LCD_MODE_CMD();
  HAL_SPI_Transmit(&SPI_HANDLE, &cmd, 1, 1000);
  LCD_CHIP_UNSELECT();
}

// 发送len个字节的数据
void LCD_Send_Data(uint8_t *data, size_t len) {
  LCD_CHIP_SELECT();
  LCD_MODE_DATA();
  HAL_SPI_Transmit(&SPI_HANDLE, data, len, 1000);
  LCD_CHIP_UNSELECT();
}

// 设置屏幕方向,横屏传入0x28,竖屏传入0x48
void LCD_Set_Oritention(uint8_t ori) {
  LCD_Send_Cmd(0x36);
  LCD_Send_Data(&ori, 1);
}

// 设置操作区的大小
void LCD_Set_Window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
  LCD_Send_Cmd(0x2a);
  LCD_Send_Data((uint8_t[]){x0 >> 8, x0 & 0xff, x1 >> 8, x1 & 0xff}, 4);
  LCD_Send_Cmd(0x2b);
  LCD_Send_Data((uint8_t[]){y0 >> 8, y0 & 0xff, y1 >> 8, y1 & 0xff}, 4);
}

// 用color颜色填充整个屏幕,这里不需要每个点指定位置,只要发送足够的数据,芯片会自动根据从上到下,从左到右的顺序绘制操作区的每个点
void LCD_Clear(uint16_t color) {
  LCD_Set_Window(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);

  LCD_Send_Cmd(0x2c);

  uint8_t color_data[2];
  color_data[0] = color >> 8;  // 高字节
  color_data[1] = color & 0xFF; // 低字节

  for (int i = 0; i < LCD_WIDTH * LCD_HEIGHT; i++) {
    LCD_Send_Data(color_data, 2);
  }
}

// LCD初始化
void LCD_Init() {

  LCD_Send_Cmd(0x3A);
  LCD_Send_Data((uint8_t[]){0x55}, 1); // 设置成RGB565格式,默认为RGB666

  LCD_Send_Cmd(0xB1); // 设置帧率
  LCD_Send_Data((uint8_t[]){0x00, 0x15}, 2); // 默认为0x1B 70Hz, 会有肉眼可见的闪烁,可以设高一点,这里设成了0x15, 90Hz

  LCD_Set_Oritention(LCD_ORIENTATION); // 根据需要设置横屏或竖屏

  LCD_Clear(DEFAULT_BACKGROUND_COLOR); //清屏
  // 退出睡眠模式
  LCD_Send_Cmd(0x11);
  HAL_Delay(10); // 手册要求至少delay 10ms

  // 开启显示
  LCD_Send_Cmd(0x29);

}

完成以上方法之后,在main.c中,调用

复制代码
LCD_Reset();
LCD_Init();

我们就可以在屏幕上看到按照DEFAULT_BACKGROUND_COLOR颜色点亮的屏幕了。

其他显示文字、图像、或者线条的方法,就是通过发送0x2c命令填充像素。如:

复制代码
// 画一个点,现将操作区设置为x, y坐标点,然后发送0x2c命令,跟上一个2字节的颜色数据,就能在屏幕的x,y坐标处显示出一个点
void LCD_Draw_Pixel(uint16_t x, uint16_t y, uint16_t color) {
  LCD_Set_Window(x, y, x, y);
  LCD_Send_Cmd(0x2c);
  uint8_t color_data[] = {color >> 8, color & 0xFF};
  LCD_Send_Data(color_data, 2);
}

// 画一条线,从(x0, y0)到(x1, y1)两点间画一条直线。利用了之前画点的函数
void LCD_Draw_Line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
  uint16_t start_x, start_y, end_x, end_y;
  if (x0 < x1) {
    start_x = x0;
    end_x = x1;
  } else {
    start_x = x1;
    end_x = x0;
  }
  
  if (y0 < y1) {
    start_y = y0;
    end_y = y1;
  } else {
    start_y = y1;
    end_y = y0;
  }

  for (uint16_t i = start_x; i <= end_x; i++) {
    for (uint16_t j = start_y; j <= end_y; j++) {
      LCD_Draw_Pixel(i, j, color);
    }
  }

}

完整项目工程在这里:https://github.com/sqlxx/stm32-workshop/ (lcd_driver分支)

相关推荐
蓝天居士4 小时前
PY32F040单片机介绍(1)
单片机·国产mcu·1024程序员节
柒月玖.5 小时前
基于AT89C52单片机的计算器设计与仿真
单片机·嵌入式硬件
乐十九6 小时前
物模型详解
嵌入式硬件
czhaii6 小时前
STC32G144K246,高速PWM@240Mhz 运行测试
stm32·单片机·fpga开发
炸膛坦客7 小时前
Cortex-M3-STM32F1 开发:(十一)ARM Cortex-M 内核中的 MPU 和 FPU
arm开发·stm32·嵌入式硬件
✎ ﹏梦醒͜ღ҉繁华落℘7 小时前
单片机开发---分层架构设计
单片机·嵌入式硬件·mongodb
国科安芯8 小时前
AS32S601ZIT2抗辐照MCU在商业卫星飞轮系统中的可靠性分析
服务器·网络·人工智能·单片机·嵌入式硬件·fpga开发·1024程序员节
应用市场8 小时前
STM32卡尔曼滤波算法详解与实战应用
人工智能·stm32·算法
优信电子9 小时前
电脑控制DFPlayer Mini MP3播放音乐
单片机·串口·嵌入式·mp3·语音播报·串口语音·mp3播报