STM32F103RCT6+0.99寸TFT圆屏 硬件SPI+DMA显示(中级)

系列文章目录

STM32F103RCT6+0.99寸TFT圆屏 硬件SPI显示(初级)

STM32F103RCT6+0.99寸TFT圆屏 硬件SPI+DMA显示(中级)

STM32F103RCT6+0.99寸TFT圆屏 硬件SPI+外部FLASH显示(高级)



前言

上一篇文章使用软件SPI成功显示,但是这个仅限于新手理解操作,只能显示静态图片,不能显示速度快的动态图片,动态显示效果不好,所以在上篇的基础,本文章将软件SPI改成硬件SPI(不知道的可以去看我上一篇的STM32F103RCT6+0.99寸TFT圆屏 软件SPI显示(初级)_中景园tft屏幕-CSDN博客),传输速度提高一倍,再采用DMA传输,速度再次提高一倍。


一、DMA简介

   DMA,Direct Memory Access—直接存储器存取,也称为成组数据传送,也称为直接内存操作。DMA在数据传送过程中,没有保存现场、恢复现场之类的工作,主要功能是用来搬数据,但是不需要占用CPU, 即在传输数据的时候,CPU可以干其他的事情,类似多线程。

  由于CPU根本不参加传送操作,因此就省去了CPU取指令、取数、送数等操作。内存地址修改、传送字 个数的计数等等,也不是由软件实现,而是用硬件线路直接实现的。所以DMA方式能满足高速I/O设备的要求,也有利于CPU效率的发挥。

  数据传输支持从外设到存储器或者存储器到存储器, 这里的存储器可以是SRAM或者是FLASH。DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,DMA2只存在于大容量产品和互联型产品中。

 想要使用DMA传输数据,必须先DMA请求。

下面是DMA请求映像表:

  DMA传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。

  举个例子,你可以理解DMA为外援,当你有搬砖、跑步任务时,你不可能同时做这两个任务,你可以叫外援帮你完成搬砖任务,你只需要外援告诉你搬了多少砖、完成进度等就行,然后你去完成跑步任务就行,这跟软件的多线程类似,但是区别就是DMA相当于两个人同时干两件事,而多线程相当于一个人分时干两件事,分时时间越短,就容易让人以为一个人同时干两件事。

二、SPI简介

  上一篇文章,忘记介绍SPI,这一篇补上。SPI(Serial Peripheral Interface)是一种同步串行通信接口(高速全双工的通信总线),通常用于电子设备中微控制器与外围设备之间的通信。该接口具有高速通信的特性,常用于嵌入式系统中与传感器、显示器、存储芯片等组件进行通信。

  STM32的SPI接口通常包括主、从模式、全双工通信以及多主机支持等特性。使用SPI接口可以实现快速的数据传输,适用于需要高速数据交换的应用场景。

 SPI通讯设备之间的常用连接方式如图:

   SPI连接只需要4根线,分别是SCK、MOSI、MISO,片选线为SS。SS ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS;SCK (Serial Clock):时钟信号线,用于通讯数据同步,它由通讯主机产生,决定了通讯的速率; MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚;MISO (Master Input,,Slave Output):主设备输入/从设备输出引脚。如图所示:

  一个主机可以挂载多个从机,通过片选SS选择不同从机传输数据。

  SPI通讯时序如下图(SPOL=0,SPHA=1):

SPI通信有4种不同的操作模式如下图所示:

 (时钟极性) CPOL=0,SCK的空闲状态为低电平;CPOL=1,SCK的空闲状态为高电平。

 (时钟相位)CPHA=0,在SCK的第一个跳变沿(上升或下降)数据被采样; CPHA=1,在SCK的第二个跳变沿(上升或下降)数据被采样。SPI 主设备与从设备通信的时钟相位和极性应该一致。

三、硬件设计

引脚改了,改成主控芯片的硬件SPI脚,STM32F103RCT6与0.99寸TFT圆屏硬件连接图如下:

STM32F103RCT60.99寸TFT圆屏
3.3VVCC
GNDGND
PB5SDA3_MOSI
PB3SCL3_SCLK
PC13RES1
PC14DC1
PB9CS1_L
PC15BLK1

将图片转成二进制数组步骤可以参考STM32F103RCT6+0.99寸TFT圆屏 软件SPI显示(初级)_中景园tft屏幕-CSDN博客

,这里不再重复论述。

四、软件解析

  采用的是硬件SPI,并用了DMA,所以速度特别快,本程序是鉴于中景园的程序上修改。这里按.c文件介绍。

①lcd_init

先配置TFT屏的引脚,按上面讲的硬件设计对应配置:

void LCD1_GPIO_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);	 //使能A端口时钟

	//RES1、DC1、BLK1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOC,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
	//CS1_L、CS1_R
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9);	
}

②spi.c

配置硬件SPI

void SPI3_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//使能GPIOA
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //改变指定管脚的映射,JTAG的其他引脚PA15 PB3、PB4有其他用途,设置关闭JTAG部分功能,保留SWD功能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);//使能SPI1	
	
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_3|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	 //复用推挽输出
	GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOA
	GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_5);

	SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;//只发送模式
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置SPI工作模式:主机模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//设置SPI数据大小:8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟空闲时SCLK位高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//串行同步时钟空第二个时钟沿捕获
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS信号由软件管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;//波特率预分频值:波特率预分频值为4
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据传输高位先行
	SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC值计算的多项式
	SPI_Init(SPI3,&SPI_InitStructure);//初始化SPI
	SPI_Cmd(SPI3, ENABLE);//使能SPI
}

③dma.c

  配置SPI的DMA,可以查看DMA请求映像表,SPI3对应的DMA是DMA2通道2,所以要配置DMA2通道2,不过要分成两种,MYDMA2_Config1是直接全部数据发送,用于发送单一数据量,比如发送单一颜色的数据,在这里用于显示单个颜色;MYDMA2_Config是边发送边传输,用于发送不同数据量,比如不同发送不同颜色的数据,在这里用于同时显示多个颜色,即显示图片。

/************************DMA2***************************/

u16 DMA2_MEM_LEN;//保存DMA每次数据传送的长度 	    
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量 
void MYDMA2_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);	//使能DMA传输
	
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
	DMA2_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
	  	
} 

void MYDMA2_Config1(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);	//使能DMA传输
	
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
	DMA2_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;  //内存地址寄存器不变
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //数据宽度为16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
} 

//开启一次DMA传输
void MYDMA2_Enable(DMA_Channel_TypeDef*DMA_CHx)
{ 
	DMA_Cmd(DMA_CHx, DISABLE );
 	DMA_SetCurrDataCounter(DMA2_Channel2,DMA2_MEM_LEN);
 	DMA_Cmd(DMA_CHx, ENABLE);
}	  

③lcd1.c

lcd1.c主要是0.99寸TFT屏显示程序,字符、字符串、中文字符、数字、图片等显示程序,这里就不一 一介绍了。

在指定区域填充颜色函数:这里用到MYDMA2_Config1来配置

/******************************************************************************
      函数说明:在指定区域填充颜色
      入口数据:xsta,ysta   起始坐标
                xend,yend   终止坐标
								color       要填充的颜色
      返回值:  无
******************************************************************************/
void LCD1_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{          
	u16 color1[1];
	u16 num;
	color1[0]=color;
	num=(xend-xsta)*(yend-ysta);
	LCD1_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
	LCD_CS1_L_Clr();
	LCD_CS1_R_Clr();
	SPI3->CR1|=1<<11;//设置SPI16位传输模式
	SPI_Cmd(SPI3, ENABLE);//使能SPI
	MYDMA2_Config1(DMA2_Channel2,(u32)&SPI3->DR,(u32)color1,num);
	SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Tx,ENABLE);
	MYDMA2_Enable(DMA2_Channel2);
	while(1)
	{
		if(DMA_GetFlagStatus(DMA2_FLAG_TC2)!=RESET)//等待通道4传输完成
		{
			DMA_ClearFlag(DMA2_FLAG_TC2);//清除通道3传输完成标志
			break;
		}
	}
	LCD_CS1_L_Set();
	LCD_CS1_R_Set();
	SPI3->CR1=~SPI3->CR1;
	SPI3->CR1|=1<<11;
	SPI3->CR1=~SPI3->CR1;//设置SPI8位传输模式
	SPI_Cmd(SPI3, ENABLE);//使能SPI 						  	    
}

显示图片函数:这里用到MYDMA2_Config来配置

/******************************************************************************
      函数说明:显示图片
      入口数据:x,y起点坐标
                length 图片长度
                width  图片宽度
                pic[]  图片数组    
      返回值:  无
******************************************************************************/
void LCD1_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
{
	u16 num;
	num=length*width*2;
	LCD1_Address_Set(x,y,x+length-1,y+width-1);
	LCD_CS1_L_Clr();
	LCD_CS1_R_Clr();
	MYDMA2_Config(DMA2_Channel2,(u32)&SPI3->DR,(u32)pic,num);
	SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Tx,ENABLE);
	MYDMA2_Enable(DMA2_Channel2);
	while(1)
	{
		if(DMA_GetFlagStatus(DMA2_FLAG_TC2)!=RESET)//等待通道4传输完成
		{
			DMA_ClearFlag(DMA2_FLAG_TC2);//清除通道3传输完成标志
			break; 
		}
	}
	LCD_CS1_L_Set();
	LCD_CS1_R_Set();		
}

④main.c

主要是初始化LCD显示函数,动态显示眼睛。图片也是转成二进制数组,把数组用DMA发给LCD,即可显示图片,添加了延时,不然显示太快了,主要是保证眼睛的显示效果。

最终显示效果如下:


总结

  通过和上一篇的通过软件SPI对比,肉眼看不出一帧一帧的显示图片了,软件SPI能明显感觉得到是一帧一帧的显示图片,硬件SPI+DMA显示图片很流畅,适合动态显示图片。

  各位如果觉得有不足之处或者有疑问,可以在评论上提建议和讨论,有空的话,会解答和修改不足之处。

代码链接如下:

https://download.csdn.net/download/weixin_45534031/89599085?spm=1001.2014.3001.5503

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要使用STM32F103RCT6驱动1.44TFT LCD显示屏,首先需要了解TFT LCD显示屏的基本工作原理和通信接口。 TFT LCD显示屏由控制器和显示面板组成,控制器负责将图像数据发送到显示面板,并控制显示面板的驱动方式。STM32F103RCT6是一款32位ARM Cortex-M3核心的微控制器,具有丰富的外设和较高的处理能力,适合用于驱动TFT LCD显示屏。 接下来需要确定TFT LCD显示屏的通信接口是什么,常见的通信接口有SPI、I2C和8080并行接口等。对于1.44TFT LCD显示屏,通常采用SPI接口进行通信。 在进行硬件连接之前,需要查看TFT LCD显示屏的规格书和STM32F103RC6的数据手册来了解各个引脚的功能和连接方式。通常,SPI接口需要如下连接:使用STM32F103RCT6SPI主机模式,分别连接STM32F103RCT6SPI主机的SCK、MISO、MOSI管脚(对应SPI1或SPI2)与TFT LCD显示屏的SCK、SDO、SDI管脚,还需要连接片选管脚,以及连接显示屏的复位管脚和片选管脚以及电源供应和地。 在软件方面,需要先配置STM32F103RCT6SPI外设,并设置通信速率、数据位长度等参数。然后,编写驱动程序,通过SPITFT LCD显示屏进行数据通信,将显示数据发送到TFT LCD显示屏,以实现图像显示的功能。 总的来说,驱动1.44TFT LCD显示屏需要进行硬件设置和软件编程。通过了解TFT LCD显示屏的通信接口和STM32F103RCT6的外设特性,并编写相应的驱动程序,就可以实现TFT LCD显示屏的驱动及图像显示功能。 ### 回答2: 要驱动1.44TFTLCD显示屏,首先要使用STM32F103RCT6开发板与显示屏进行连接。我们可以通过GPIO口实现数据线与显示屏相连,而控制线则通过专门的引脚连接。 接下来需要编写相关的驱动代码来控制显示屏。我们可以使用HAL库提供的函数,来设置GPIO口的输出模式,以及数据传输相关的操作。利用SPI或者I2C通信协议,我们可以通过发送命令和数据来控制显示屏的显示内容。 在驱动代码中,我们需要先初始化SPI或者I2C通信,并配置相应的参数,比如时钟频率、传输模式等。 接着,我们需要编写命令和数据的发送函数,通过SPI或I2C发送相关指令给显示屏。比如,我们可以通过发送命令来设置显示区域、背光亮度等参数。 最后,我们可以编写显示函数来实现在显示屏上显示图像或者文字。通过发送数据来更新显示内容,并且可以设置不同的显示模式,如图形显示、字符显示等。 需要注意的是,针对不同的显示屏型号,具体的驱动代码可能会有所不同。因此,在编写驱动代码时,需要结合显示屏的数据手册来编写相应的控制指令和函数。 以上是关于如何驱动1.44TFTLCD显示屏的大致步骤。在实际应用中,还需要对具体的硬件结构和功能要求进行详细调试和开发。 ### 回答3: STM32F103RCT6是一款ARM Cortex-M3内核的微控制器,可以用于控制各种外设和驱动显示屏。在驱动1.44TFT LCD显示屏时,我们需要按照以下步骤进行设置和编程: 1. 连接硬件:首先,将STM32F103RCT6与1.44TFT LCD显示屏连接起来。根据显示屏和微控制器的规格书或引脚配置表,将引脚正确连接。 2. 配置GPIO引脚:在STM32F103RCT6的寄存器中,通过设置GPIO引脚的模式和速度,将其配置为输出模式。将对应的引脚配置为控制显示屏的D/C引脚(用于选择数据/指令)、CS引脚(用于片选)、以及时钟引脚和数据引脚等。 3. 编写驱动程序:使用C语言或其他适合的编程语言,编写代码来控制和驱动1.44TFT LCD显示屏。根据显示屏的数据手册和命令集,实现显示屏的初始化、清屏、写入像素数据等功能。 4. 设置显示屏参数:根据需要,设置显示屏的相关参数,例如屏幕分辨率、颜色模式、显示方向等。这些参数需要根据具体的显示屏型号来设置。 5. 显示数据:通过驱动程序发送像素数据,将需要显示的图像或文本等内容写入显示屏。 6. 刷新显示:根据需要,定时刷新显示屏内容,以保持实时更新的效果。 通过以上步骤,我们可以实现STM32F103RCT6对1.44TFT LCD显示屏的驱动。需要根据具体的硬件显示屏型号进行适配和调试,确保驱动程序的正确性和稳定性。同时,根据项目的需求和复杂度,可能需要更详细的代码和配置设置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值