首页 > 其他分享 >BUG分享|DMA发送数据时,会被莫名打断,或者发送乱码。

BUG分享|DMA发送数据时,会被莫名打断,或者发送乱码。

时间:2023-12-29 14:33:39浏览次数:42  
标签:DMA u8 color 乱码 SPI InitStructure 发送数据 Fill

引言

在驱动ST7789屏幕时,使用了SPI+DMA进行图像刷新。在执行清屏操作时,使用配置DMA内存到外设,内存地址不变,发送的内存是一个16位的RGB565像素值变量,可以指定清屏填充的颜色。
单片机:STM32F411CEU6
库函数:标准库

现象

清屏代码如下:

/* 清屏函数 输入参数填充矩形的左上角坐标和右下角坐标以及颜色(RGB565)*/
void vST7789V3_Fill(u8 xsta,u8 ysta,u8 xend,u8 yend,u16 color)
{
	u16 num;
	num=(xend-xsta)*(yend-ysta);
	vST7789V3_Address_Set(xsta,ysta,xend-1,yend-1);// 设置显示范围
	/* 配置DMA */
	SPI_DMA_Fill_Config((u32)&color,num);

	/* 启动DMA */
	SPI_DMA_Enable();
}

/* DMA配置函数*/
/*
	SPI1_DMA1传输参数配置
	用于色块填充
	内存->外设 内存地址不变 为16位RGB565色度值
输入参数cmar:色块值地址
输入参数cmar:数据长度(填充的像素数量)
*/
void SPI_DMA_Fill_Config(u32 cmar,u16 cndtr)
{
	DMA_InitTypeDef DMA_InitStructure;

	SPI_DataSizeConfig(SPI1, SPI_DataSize_16b);		//设置SPI发送数据宽度16位 注:传输完成需要改成8位宽
	DMA1_MEM_LEN=cndtr;

	// DMA配置
	/*SPI1发送功能的DMA在DMA2通道2数据流2中,在STM32F411参考手册170页*/
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能

	DMA_DeInit(DMA2_Stream2);
	while (DMA_GetCmdStatus(DMA2_Stream2) != DISABLE){}//等待 DMA可配置

	/* 配置 DMA Stream */
	DMA_InitStructure.DMA_Channel = DMA_Channel_2;  			//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR;  //DMA外设SPI基地址
	DMA_InitStructure.DMA_Memory0BaseAddr = cmar;				//DMA 存储器0地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;		//存储器到外设模式
	DMA_InitStructure.DMA_BufferSize = cndtr;					//数据传输量
	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_VeryHigh;						//中等优先级
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;						//FIFO模式禁止
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;				//FIFO 阈值
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; 				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 		//外设突发单次传输
	DMA_Init(DMA2_Stream2, &DMA_InitStructure);									//初始化DMA Stream
}

使用时,直接调用如vST7789V3_Fill(0,0,240,135,0x0000);开启DMA传输,将屏幕填充黑色。DMA传输完成大概10ms。

/* 代码段1 */
while(1)
{
	vST7789V3_Fill(0,0,240,135,0xffff);
	while(1);
}

代码段1运行,一切正常,屏幕全白。当在调用填充函数之后,添加其他功能,如串口打印时,屏幕就乱了。

/* 代码段2 */
while(1)
{
	vST7789V3_Fill(0,0,240,135,0xffff);
	printf("hello world");
	while(1);
}

代码段2运行效果:
image
可以看到,在显示了3个像素点之后,数据就开始不对了。填充整屏时间大概10ms,添加延时函数试一下。

/* 代码段3 */
while(1)
{
	vST7789V3_Fill(0,0,240,135,0xffff);
	delay_ms(5);
	printf("hello world");
	while(1);
}

代码段3运行效果:
image
加入了延时5ms之后,数据在延时时间内是正常的,延时结束,调用串口打印时,出问题了。

分析

正常来说,DMA传输时,是不占用CPU的,CPU可以干其他事情,互不干扰。但是目前现象是,串口发送造成了DMA发送错误。后续更换了其他函数,都会影响屏幕的显示。必须使用延时函数,在DMA发送期间,CPU只去计数,其他事情都不能干(试过控制LED闪烁,也是可以的)。
考虑半天,对比两个显示结果,发现端倪。LCD屏幕重新上电后会显示之前的画面,如果有新的显示数据写入,会覆盖在上一个画面之上。代码段2的结果屏幕大部分是蓝色,而运行代码段3时,白色画面覆盖了半个屏幕,如果在中途中断了,后续没有覆盖的部分应该也是蓝色,可是这里是绿色。说明DMA实际上是没有被打断的,后续的绿色也是DMA写入的数据。
那么问题到这里明朗了些,检查DMA发送的内存数据。这里由于是色块填充函数,所以只需要输入颜色的数据,DMA自动的只发送一个固定的16位数据。检查这个16位数据,发现了问题所在。由于这个函数是从SPI循环发送改成了DMA发送,所以没有注意到这个bug。

/* 清屏函数 输入参数填充矩形的左上角坐标和右下角坐标以及颜色(RGB565)*/
void vST7789V3_Fill(u8 xsta,u8 ysta,u8 xend,u8 yend,u16 color)
{
	u16 num;
	num=(xend-xsta)*(yend-ysta);
	vST7789V3_Address_Set(xsta,ysta,xend-1,yend-1);// 设置显示范围
	/* 配置DMA */
	SPI_DMA_Fill_Config((u32)&color,num);

	/* 启动DMA */
	SPI_DMA_Enable();
}

问题出在这个函数里,输出参数中color是16位颜色数据,而SPI_DMA_Fill_Config(u32 cmar,u16 cndtr)中,cmar是DMA搬运数据的内存地址。这里将输出参数color直接取地址传入DMA配置参数,当启动了DMA之后,这个函数就消灭了,随之申请在栈空间的变量color也会被自动释放掉。这时,&color这个指针就是野指针了,这块内存如果被后续的函数申请,会被改变内容。如果在DMA传输的过程中被改变了,当然DMA发送的内容也会改变。这里的致命错误就是将栈空间的地址给了DMA。

解决方案

修改填充函数,将16位的色彩参数改成色彩的地址,在外部定义全局色彩变量,这里的变量是存储在静态存储区,不会消失。每次传入色彩变量的地址即可。

const u8 LCD_black = 0x0000;
const u8 LCD_white = 0xffff;
void vST7789V3_Fill(u8 xsta,u8 ysta,u8 xend,u8 yend,u8* colorAddr)
{
	u16 num;
	num=(xend-xsta)*(yend-ysta);
	vST7789V3_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
	SPI_DMA_Fill_Config((u32)colorAddr,num);
	SPI_DMA_Enable();
}

但是,上述方法只适合于固定预设好的色彩值,且如果色彩太多,需要定义的全局变量也会增多。我们的问题就是函数的输入参数会在函数结束时消失,那我们不让他消失就可以了,另一种方案如下:

//}
void vST7789V3_Fill(u8 xsta,u8 ysta,u8 xend,u8 yend,u16 color)
{
	u16 num;
	/* 申请静态局部变量 函数结束时不会被释放 */
	static u16 save_color;
	save_color = color;
	num=(xend-xsta)*(yend-ysta);
	vST7789V3_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
	SPI_DMA_Fill_Config((u32)&save_color,num);
	SPI_DMA_Enable();
}

这里申请了一个静态局部变量,普通的局部变量是存储在栈空间的,函数结束会释放。添加了static的修饰,变量将存储在静态存储区,即使函数结束,变量以及存储的内容依然存在。这样就可以保证DMA在传输时,该变量不会改变。

标签:DMA,u8,color,乱码,SPI,InitStructure,发送数据,Fill
From: https://www.cnblogs.com/lxy0558/p/17934401.html

相关文章

  • kafka消费中文显示为乱码
    1.情景展示如上图所示,在windows操作系统当中,当我使用消费主题的命令进行数据消费时,存在kafka当中的消息含有的中文,最终展示为乱码。kafka-console-consumer.bat--bootstrap-serverlocalhost:9092--topictopic-xxx-63.库名.表名--from-beginning这是怎么回事?如何解决?2......
  • 上传文件名 乱码 (后端)
     问题:上传文件后文件名变成中文乱码,在排除后发现是后端的问题;解决:在获取文件后的位置,修改:添加文件名= newString(originalFilename.getBytes("ISO-8859-1"),"UTF-8")例:1for(MultipartFilefile:uploadFile){2......
  • intel网卡系列及RDMA支持
    起源,intel网卡中的SFP+是啥?SFP+是光纤模块,是一种可热插拔的,独立于通信协议的光学收发器。SFP+光纤模块是SFP的升级。SFP+光纤模块在以太网和1G、2G、4G光纤通道上SFP已经得到了广泛应用。SFP+为了适应更高的数据速率,设计了比SFP增强的电磁屏蔽与信号保护特性。 intel800系列......
  • 程序员学习网站推荐:路线向导(roadmap.sh)
    网站地址:https://roadmap.sh/在外网的技术论坛上看到这个网站,上面给出多种编程语言的学习路线,也就是给出不同编程语言的从易到难的组成内容(语言特性),通过这个网站可以辅助学习编程语言。比如学习python语言:这个网站并不能给出你不同编程语言的具体学习内容,但是它会给出......
  • 20基于XDMA实现PCIE通信方案
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述本方案内容作为通用的教程内容,适合XILINX各类支持PCIE通信的板卡。并且米联客在XDMA中使用了自己编写的......
  • 14 fdma数据通路加入sobel算法IP方案
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述    本文实验目的:1:掌握2个uifdma_dbufIP的同时使用,以及读写通道之间的同步设计2:实现1路数据实......
  • 12使用fdma读写DDR
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述    FDMA是米联客基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直......
  • 13基于fdma ddr多路视频数据构架方案
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述    基于AXI总线可以使用axi_interconnect的仲裁机制,同时接入多个基于AXI总线的IP,米联客的fdma采......
  • 使用aiohttp异步调用API+request上传文件中文文档名乱码解决方案
    有时候在调用需要用异步调用API接口。在python中有很多框架,比如asyncio,Celery,Quart等。这里我选择了asyncio。Python3.5以上版本内置了asyncio库,可以用来编写单线程的并发代码。可以使用此库与aiohttp结合来发送异步HTTP请求。Python调用案例GETimportasyncioimportaio......
  • Linux Debian12使用podman安装upload-labs靶场环境
    一、upload-labs简介PHP语言编写,持续收集渗透测试和CTF中针对文件上传漏洞的靶场,总共21关,每一关都包含着不同的上传绕过方式。二、安装podman环境LinuxDebian系统如果没有安装podman容器环境,可以参考这篇文章先安装podman环境,LinuxDebian11使用国内源安装Podman环境三、pod......