【程序】STM32F103VE单片机通过FSMC驱动Risym 2.8寸TFTLCD 320x240分辨率 ILI9325/ILI9341彩屏(ID寄存器读出来为0就说明驱动芯片是ILI9341)

16 篇文章 7 订阅

前段时间在淘宝的Risym旗舰店买了一款2.8寸的TFTLCD彩屏,分辨率为320x240。淘宝资料上写着这款液晶屏的控制芯片是ILI9325或ILI9328。

卖家给的资料是一个压缩包,以前下载下来还能打开。现在下载下来打开提示“不可预料的压缩文件末端”,压缩文件是坏的。打开以前下载的压缩包,里面有两个例程:“STM32_FSMC_TFT_20130112-OK-MDK”和“STM32-ucosII+GUI3.9a-20121220-MDK”。运行第一个不带操作系统的例程,发现根本运行不了,液晶屏一直是白屏。

调试时发现里面有这样一句话:

DeviceCode = LCD_ReadReg(0x0000);

读取0号寄存器上的设备ID,读出来一直是0。在这个上面卡了很久,后面找了野火的3.2寸彩屏的例程,里面就没有读ID的操作,全部都是写操作,而且那个例程不仅仅能够驱动野火的彩屏,还能驱动这款彩屏,代码完全一样。

这说明,Risym卖家给的彩屏资料和例程是错误的。

实际上,这是因为这款液晶屏的实际驱动芯片是ILI9341,不是淘宝商品资料上面写的9325/9328,所以才读不出来ID号。真正的ILI9325(比如微雪的3.2寸彩屏)是能够从0号寄存器上读出ID号为0x9325的!

以下给出该款彩屏正确的程序。这个程序能正常轮播显示4张彩色图片,程序中没有任何读操作,读所谓的0号ID寄存器的结果为0。

ILI9341彩屏程序下载地址:https://pan.baidu.com/s/1Uy7z4H0sBBAAWU8U2o_hZQ(提取码:nfmm)

关于ILI9325彩屏请参阅:https://blog.csdn.net/ZLK1214/article/details/107327515

【main.c】

#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"
#include "images.h"
#include "ILI9341.h"

int main(void)
{
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  printf("STM32F103VE FSMC ILI9341\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  ILI9341_Init();
  ILI9341_Clear(ILI9341_COLOR_BLUE);
  ILI9341_Enable(1);
  
  while (1)
  {
    HAL_Delay(1000);
    ILI9341_DrawImage(image1, 0, 36, 239, 248);
    HAL_Delay(1000);
    ILI9341_DrawImage(image2, 0, 36, 239, 248);
    HAL_Delay(1000);
    ILI9341_DrawImage(image3, 0, 36, 239, 248);
    HAL_Delay(1000);
    ILI9341_DrawImage(image4, 0, 36, 239, 248);
  }
}

【ILI9341.h】

#ifndef _ILI9341_H
#define _ILI9341_H

#define ILI9341_COLOR_BLACK 0x0000
#define ILI9341_COLOR_BLUE 0x001f
#define ILI9341_COLOR_GREEN 0x07e0
#define ILI9341_COLOR_RED 0xf800
#define ILI9341_COLOR_WHITE 0xffff

#define ILI9341_CMD (*(volatile uint16_t *)0x60000000)
#define ILI9341_DATA (*(volatile uint16_t *)0x60020000)

#define ILI9341_CMD_BEGINPAINT 0x2c

void ILI9341_Clear(uint16_t color);
void ILI9341_DrawImage(const void *image, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
void ILI9341_Enable(int enabled);
void ILI9341_Init(void);
void ILI9341_SetRegion(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void ILI9341_SetScanDirection(uint8_t direction);

#endif

【ILI9341.c】

#include <stm32f1xx.h>
#include "ILI9341.h"

#define BL_OFF HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET)
#define BL_ON HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET)
#define RST_OFF HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET)
#define RST_ON HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET)

NOR_HandleTypeDef hnor;
static int lcd_cx, lcd_cy;

/* 清屏 */
void ILI9341_Clear(uint16_t color)
{
  int i = lcd_cx * lcd_cy;
  
  ILI9341_SetRegion(0, 0, lcd_cx - 1, lcd_cy - 1);
  ILI9341_CMD = ILI9341_CMD_BEGINPAINT;
  while (i--)
    ILI9341_DATA = color;
}

/* 显示图片 */
void ILI9341_DrawImage(const void *image, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
  const uint16_t *p = image;
  int i = width * height;
  
  ILI9341_SetRegion(x, y, x + width - 1, y + height - 1);
  ILI9341_CMD = ILI9341_CMD_BEGINPAINT;
  while (i--)
    ILI9341_DATA = *p++;
}

/* 开/关显示 */
void ILI9341_Enable(int enabled)
{
  if (enabled)
  {
    HAL_Delay(20); // 延时打开背光, 避免瞬间出现白色条纹
    BL_ON;
  }
  else
    BL_OFF;
}

/* 初始化显示屏 */
void ILI9341_Init(void)
{
  FSMC_NORSRAM_TimingTypeDef timing = {0};
  GPIO_InitTypeDef gpio;
  
  __HAL_RCC_FSMC_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  
  // PC5: BL
  BL_OFF;
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Pin = GPIO_PIN_5;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD12: RST
  RST_ON;
  gpio.Pin = GPIO_PIN_12;
  HAL_GPIO_Init(GPIOD, &gpio);
  
  // PD0~1: FSMC_D2~3, PD4: FSMC_NOE, PD5: FSMC_NWE, PD7: FSMC_NE1, PD8~11: FSMC_D13~16, PD14~15: FSMC_D0~1
  // FSMC_NOE=>RD, FSMC_NWE=>WR, FSMC_D16=>RS, FSMC_NE1=>CS
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_14 | GPIO_PIN_15;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOD, &gpio);
  
  // PE7~15: FSMC_D4~12
  gpio.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
  HAL_GPIO_Init(GPIOE, &gpio);
  
  // FSMC一共有6种模式:模式1~2和模式A~D
  // 彩屏使用的是模式2(即NOR Flash模式)而非模式B,这是因为配置FSMC时并没有使能扩展模式
  hnor.Extended = FSMC_Bank1E;
  hnor.Instance = FSMC_Bank1;
  hnor.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hnor.Init.MemoryType = FSMC_MEMORY_TYPE_NOR;
  hnor.Init.NSBank = FSMC_NORSRAM_BANK1;
  hnor.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  timing.AddressHoldTime = 1;
  timing.AddressSetupTime = 0;
  timing.CLKDivision = 2;
  timing.DataLatency = 2;
  timing.DataSetupTime = 1;
  HAL_NOR_Init(&hnor, &timing, NULL);
  
  HAL_Delay(1);
  RST_OFF;
  HAL_Delay(1);
  
  // Power control B
  ILI9341_CMD = 0xcf;
  ILI9341_DATA = 0x00;
  ILI9341_DATA = 0x81;
  ILI9341_DATA = 0x30;
  
  // Power on sequence control
  ILI9341_CMD = 0xed;
  ILI9341_DATA = 0x64;
  ILI9341_DATA = 0x03;
  ILI9341_DATA = 0x12;
  ILI9341_DATA = 0x81;
  
  // Driver timing control A
  ILI9341_CMD = 0xe8;
  ILI9341_DATA = 0x85;
  ILI9341_DATA = 0x10;
  ILI9341_DATA = 0x78;
  
  // Power control A
  ILI9341_CMD = 0xcb;
  ILI9341_DATA = 0x39;
  ILI9341_DATA = 0x2c;
  ILI9341_DATA = 0x00;
  ILI9341_DATA = 0x34;
  ILI9341_DATA = 0x02;
  
  // Pump ratio control
  ILI9341_CMD = 0xf7;
  ILI9341_DATA = 0x20;
  
  // Driver timing control B
  ILI9341_CMD = 0xea;
  ILI9341_DATA = 0x00;
  ILI9341_DATA = 0x00;
  
  // Frame Rate Control (In Normal Mode/Full Colors)
  ILI9341_CMD = 0xb1;
  ILI9341_DATA = 0x00;
  ILI9341_DATA = 0x1b;
  
  // Display Function Control
  ILI9341_CMD = 0xb6;
  ILI9341_DATA = 0x0a;
  ILI9341_DATA = 0xa2;
  
  // Power Control 1
  ILI9341_CMD = 0xc0;
  ILI9341_DATA = 0x35;
  
  // Power Control 2
  ILI9341_CMD = 0xc1;
  ILI9341_DATA = 0x11;
  
  // VCOM Control 1
  ILI9341_CMD = 0xc5;
  ILI9341_DATA = 0x45;
  ILI9341_DATA = 0x45;
  
  // VCOM Control 2
  ILI9341_CMD = 0xc7;
  ILI9341_DATA = 0xa2;
  
  // Enable 3G
  ILI9341_CMD = 0xf2;
  ILI9341_DATA = 0x00;
  
  // Gamma Set
  ILI9341_CMD = 0x26;
  ILI9341_DATA = 0x01;
  
  // Positive Gamma Correction
  ILI9341_CMD = 0xe0;
  ILI9341_DATA = 0x0f;
  ILI9341_DATA = 0x26;
  ILI9341_DATA = 0x24;
  ILI9341_DATA = 0x0b;
  ILI9341_DATA = 0x0e;
  ILI9341_DATA = 0x09;
  ILI9341_DATA = 0x54;
  ILI9341_DATA = 0xa8;
  ILI9341_DATA = 0x46;
  ILI9341_DATA = 0x0c;
  ILI9341_DATA = 0x17;
  ILI9341_DATA = 0x09;
  ILI9341_DATA = 0x0f;
  ILI9341_DATA = 0x07;
  ILI9341_DATA = 0x00;
  
  // Negative Gamma Correction
  ILI9341_CMD = 0xe1;
  ILI9341_DATA = 0x00;
  ILI9341_DATA = 0x19;
  ILI9341_DATA = 0x1b;
  ILI9341_DATA = 0x04;
  ILI9341_DATA = 0x10;
  ILI9341_DATA = 0x07;
  ILI9341_DATA = 0x2a;
  ILI9341_DATA = 0x47;
  ILI9341_DATA = 0x39;
  ILI9341_DATA = 0x03;
  ILI9341_DATA = 0x06;
  ILI9341_DATA = 0x06;
  ILI9341_DATA = 0x30;
  ILI9341_DATA = 0x38;
  ILI9341_DATA = 0x0f;
  
  // Pixel Format Set
  ILI9341_CMD = 0x3a;
  ILI9341_DATA = 0x55;
  
  // Sleep Out
  ILI9341_CMD = 0x11;
  HAL_Delay(100);
  
  ILI9341_SetScanDirection(0); // 设置扫描方向
  ILI9341_CMD = 0x29; // 允许显示显存中的图像 (后面还需要打开背光才能开显示)
}

/* 设置显示区域 */
void ILI9341_SetRegion(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
  ILI9341_CMD = 0x2a;
  ILI9341_DATA = x0 >> 8;
  ILI9341_DATA = x0 & 0xff;
  ILI9341_DATA = x1 >> 8;
  ILI9341_DATA = x1 & 0xff;
  
  ILI9341_CMD = 0x2b;
  ILI9341_DATA = y0 >> 8;
  ILI9341_DATA = y0 & 0xff;
  ILI9341_DATA = y1 >> 8;
  ILI9341_DATA = y1 & 0xff;
}

/* 设置GRAM扫描方向 */
void ILI9341_SetScanDirection(uint8_t direction)
{
  assert_param(direction <= 7);
  
  // 根据模式更新XY方向的像素宽度
  if (direction % 2 == 0)
  {
    // 0 2 4 6模式下X方向像素宽度为240, Y方向为320
    lcd_cx = 240;
    lcd_cy = 320;
  }
  else        
  {
    // 1 3 5 7模式下X方向像素宽度为320,Y方向为240
    lcd_cx = 320;
    lcd_cy = 240;
  }
  
  // 0x36命令参数的高3位可用于设置GRAM扫描方向
  ILI9341_CMD = 0x36;
  ILI9341_DATA = 0x08 | (direction << 5);
}

 

查看一下芯片的手册,可以发现0号命令根本就不是读取芯片ID的命令,而是一个空操作(No Operation)。

那是不是此屏不支持读操作呢?事实并非如此,我们可以通过Memory Read命令将彩屏上显示的内容读回单片机。

在下面的程序中,我们先将图片显示到屏幕上,然后将屏幕显示内容的前120行读出来,和原来的图像数据比较,看是否一样。如果一样,则说明读操作没有问题。

#include <stdio.h>
#include <stm32f1xx.h>
#include <string.h>
#include "common.h"
#include "images.h"
#include "ILI9341.h"

#define RGB888TO565(color) ((((color) >> 8) & 0xf800) | (((color) >> 5) & 0x7e0) | (((color) >> 3) & 0x1f))

static void ILI9341_GetPixels(uint16_t *pixels, int count)
{
  int i = 0;
  uint16_t data[3];
  uint16_t rgb565[2];
  uint32_t rgb888[2];
  
  ILI9341_CMD = 0x2e;
  ILI9341_DATA; // dummy read
  while (i < count)
  {
    // 读两个像素, 每个像素3字节
    // 每字节表示一个分量, 分量在字节中是左对齐的
    data[0] = ILI9341_DATA; // 0xr1g1 (高字节为第一个像素的红色分量, 低字节为第一个像素的绿色分量)
    data[1] = ILI9341_DATA; // 0xb1r2 (高字节为第一个像素的蓝色分量, 低字节为第二个像素的红色分量)
    data[2] = ILI9341_DATA; // 0xg2b2 (高字节为第二个像素的绿色分量, 低字节为第二个像素的蓝色分量)
    
    // 转换成RGB888
    rgb888[0] = (data[0] << 8) | (data[1] >> 8);
    rgb888[1] = ((data[1] & 0xff) << 16) | data[2];
    //printf("#%06X #%06X => ", rgb888[0], rgb888[1]);
    
    // 再转换成RGB565
    rgb565[0] = RGB888TO565(rgb888[0]);
    rgb565[1] = RGB888TO565(rgb888[1]);
    //printf("0x%04x 0x%04x\n", rgb565[0], rgb565[1]);
    
    // 保存颜色值
    pixels[i++] = rgb565[0];
    if (i < count)
      pixels[i++] = rgb565[1];
  }
}

static void ILI9341_GetPixelsInRect(void *pixels, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
  ILI9341_SetRegion(x, y, x + width - 1, y + height - 1);
  ILI9341_GetPixels(pixels, width * height);
}

int main(void)
{
  static uint16_t pixels[120][239];
  
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  printf("STM32F103VE FSMC ILI9341\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  ILI9341_Init();
  ILI9341_Clear(ILI9341_COLOR_BLUE);
  ILI9341_Enable(1);
  
  ILI9341_DrawImage(image1, 0, 36, 239, 248); // 在屏幕上显示一张图片
  ILI9341_GetPixelsInRect(pixels, 0, 36, 239, 120); // 读取屏幕上的图像内容
  if (memcmp(pixels, image1, sizeof(pixels)) == 0) // 和原图比较
    printf("same\n"); // 相同
  else
    printf("different\n"); // 不相同
  
  while (1)
  {
  }
}

程序运行结果为:

STM32F103VE FSMC ILI9341
SystemCoreClock=72000000
same

所以,读操作没有问题。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于驱动 TFT LCDFSMC(Flexible Static Memory Controller)在 STM32F407 上的使用,你可以按照以下步骤进行操作: 1. 配置 FSMC 控制器:首先,你需要配置 FSMC 控制器以与 TFT LCD 进行通信。这涉及到配置控制线、地址线和数据线等。你可以参考 STM32F407 参考手册中的 FSMC 章节,了解如何正确配置 FSMC 控制器。 2. 配置 TFT LCD 控制器:根据 TFT LCD 的规格书或者供应商提供的资料,你需要了解 TFT LCD 的时序要求和控制信号定义。然后,根据这些信息配置 FSMC 控制器的时序参数,以确保与 TFT LCD 的正确通信。 3. 编写驱动代码:在配置完 FSMC 控制器和 TFT LCD 控制器后,你需要编写驱动代码来实现绘制图形、显示文本等功能。这通常涉及到像素点的写、区域填充、字体显示等操作。你可以使用 C 语言或者汇编语言来编写这些代码。 4. 调试和优化:一旦你完成了驱动代码的编写,你需要通过调试和优化来确保驱动的正确性和性能。你可以使用逻辑分析仪或者示波器来观察信号波形,以确保与 TFT LCD 的通信正常。 需要注意的是,TFT LCD驱动方式和接口可能因不同的型号而有所不同,因此在开始驱动之前,最好详细阅 TFT LCD 的规格书,并参考 STM32F407 参考手册中的相关章节进行配置。此外,也可以参考一些开源项目或者社区提供的代码和资料,以便更好地理解和实现 TFT LCD驱动

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值