波形刷新方案

目录

前言

一、实时刷新与触发模式刷新

1.1 实时刷新

1.2 触发模式刷新

二、利用缓存刷新LCD


前言

这篇文章跟着上一篇“STM32示波器设计”讨论一下具体的波形刷新。

主要内容就是以下三点:

一、实时刷新

二、触发模式刷新

三、利用缓存刷新LCD


一、实时刷新与触发模式刷新

实时刷新和触发模式刷新,都是从数据处理的角度的分类的。

注意,本篇文章,无论任何情况下都是在波形刷新之前启动下一次的ADC采集传输

前提概念和参数:

采样点:每采样一个数据,即是一个采样点

波形点:由若干个采样点组成一个波形点,可以是1个,也可以是多个取平均。

波形记录长度:一条波形数据的波形点个数,即为波形记录长度

比如,我这里的硬件环境是:

1、

单通道ADC+240*320屏幕(波形显示区域为200*300),

那么,波形点就是一个采样点,波形记录长度为300。

2、

LCD写入时序设置为从左到右,从上到下,写入240*320即全屏需要28ms,写入200*300即波形显示区域需要22ms。

X\Y轴方向都以25为一小格,横向8格,纵向12格。

1.1 实时刷新

能不能实时刷新,取决于屏幕刷新率,也即屏幕刷新耗时。

这里刷新耗时22ms(固定22ms,实现方法在下面),则采完一条波形的耗时只要大于22ms,就能够实时刷新。

(为什么是大于?当采样耗时小于22ms,每次采样结束进行刷新时,这个刷新时间内本身就错过了一部分周期。当采样耗时大于22ms,由于ADC采集传输可以使用DMA与屏幕刷新同步进行,屏幕刷新时不会耽误ADC的采集)

对应的时基挡位就是,22*1000/300us=每个波形点73us,一格(25个点)1833us,接近2ms,

那么这里就取每格2ms为分界线

在2ms及以上的挡位时,在波形刷新之前启动下一次的ADC采集传输,刷新结束之后等待ADC采集完成,这样就能保证固定的刷新频率。

最理想的情况就是,将采集耗时压到最大直至接近刷新时间,这样每当刷完一条数据,下一次的ADC数据就已经准备好了。

//配置之前先关闭ADC
ADC_Stop();
//设置ADC的DMA接收buf和长度
ADC_BufSet(AdcBuf, OSC_DEPTH);
//设置定时触发频率,即采集频率
ADC_TimeSet(List_psc[OscData.UnitT], List_cnt[OscData.UnitT]);
//启动ADC采集
ADC_Start();

对单通道ADC采集的实时模式刷新,OSC_DEPTH 存储深度可以设置为300,即采300个点刷300个点。

无论哪种方式,代码中都要有一个WaveBuf,缓存归一化之后的波形数据(数组长度即波形区域宽度),用于LCD刷新。

for(int i = 0; i < OSC_DEPTH; i++)
{
    //将ADC采集数据归一化为0到200的Y值坐标
    vol = buf[i]*3300/4096/OscData.UnitV;
    if(vol > OSC_HEIGHT)
		OscData.WaveBuf[j] = OSC_HEIGHT;
	else
		OscData.WaveBuf[j] = vol;
    //计算最大值和最小值
	if(Max < vol) 
		Max = vol;
	if(Min > vol) 
		Min = vol;
}

1.2 触发模式刷新

对于2ms以上的,可以用实时模式进行刷新。到了2ms以下时,一般使用触发模式来进行刷新。

触发模式下:OSC_DEPTH 存储深度可以设置为300的整数倍,超过1K时长度随意。比如这里取600。其它的ADC参数还是一样的。

所不同的是数据处理方法,我们一般判断的是通过软件判断上升沿或下降沿并记录(硬件方式会造成的频繁的中断影响处理速度和刷新速度,只能适用于低频信号)。

如下代码,

处理逻辑:先找到第一个下降沿位置A,如果找到了则就从A位置开始取300个点,进行计算刷新。找不到则取前300个点数据进行刷新。

注意:处理时的起始索引并不是0,因为在原始的ADC采集时,总有前1到2个数据偏差较大,这里直接取到了10。

//Find第一个下降沿
Voltage = OscData.Trigger*4096/3300;
for(i = 10; i < OSC_DEPTH - OSC_WIDTH; i++){
	if(buf[i-1] > Voltage && buf[i] < (Voltage + 1)){
	//if(AdcBuf[i-1] > Voltage && AdcBuf[i] < Voltage){
		Offset = i;
		break;
	}
}

//取从第一个下降沿开始的OSC_WIDTH个数据
Max = 0; Min = 4095;
for(i = Offset, j = 0; i < Offset + OSC_WIDTH; i++, j++){
	vol = buf[i]*3300/4096/OscData.UnitV;
	if(vol > OSC_HEIGHT)
		OscData.WaveBuf[j] = OSC_HEIGHT;
	else
		OscData.WaveBuf[j] = vol;
	if(Max < vol) 
		Max = vol;
	if(Min > vol) 
		Min = vol;
}

void OscDrawWindow(uint8_t WaveBuf[]);

无论哪种方式,都是调用的同一个API进行屏幕刷新,外部只需要计算好归一化数据。

二、利用缓存刷新LCD

这里直接用了300*200*2大小的数组作为缓存,各位可以自己设计算法节省内存或者利用外部存储。

具体流程如下:

1. 第一步先清0

2. 先计算网格点,对网格点赋颜色

3. 第三步,根据X、Y值坐标,计算每两个点的Y坐标差的绝对值和最小Y坐标,进而计算出在该X轴上需要画几个点(赋颜色)

4. 将数据送入lcd_buf刷新函数取刷新

//波形显示区域|利用宏定义进行处理|320*240|300*200
#define OSC_HEIGHT					200 			//波形显示窗口高度
#define OSC_WIDTH   				300				//波形显示窗口宽度
#define OSC_DEPTH					(OSC_WIDTH*2)	//波形数据深度
#define X0							(0+9)
#define X1							(0+OSC_WIDTH+9)
#define Y0							(0+20)
#define Y1							(0+OSC_HEIGHT+20)

//像素点位置及个数计算
#define OSC_PIXEL_GetMin(X1,X0) 	((X1>=X0)?(OSC_HEIGHT-X1):(OSC_HEIGHT-X0))
#define OSC_PIXEL_GetNum(X1,X0) 	((X1>X0)?(X1-X0):((X1==X0)?1:X0-X1))

static uint16_t WaveBufBit[OSC_HEIGHT][OSC_WIDTH];

void OscDrawWindow(uint8_t WaveBuf[])
{
	uint16_t i,j,X,y;
	uint16_t LCD_Y_Min, LCD_Y_Num, LCD_Y_Line;
	
//	//统计运行时间1
//	static uint16_t LCD_Time1 = 0,LCD_Time2 = 0;
//	static uint32_t LCD_Sum[2] = {0},LCD_Cnt[2] = {0};
//	LCD_Time1 = OSTime;
	
	//默认全灭
	memset(WaveBufBit, List_c[OSC_M_WAVE].Back, OSC_WIDTH*OSC_HEIGHT*2);
	
	//计算网格点
	for(i = 1; i < 12; i++)
	{
		for(j = 1; j < 200 - 1; j++){
			if(i!=6) j++;
			WaveBufBit[j][25*i] = List_c[OSC_M_CELL].Point;
		}
	}
	for(i = 1; i < 8; i++)
	{
		for(j = 1; j < 300 - 1; j++){
			if(i!=4) j++;
			WaveBufBit[25*i][j] = List_c[OSC_M_CELL].Point;
		}
	}
	
	//计算点亮点
	for(X = 2; X < OSC_WIDTH; X++)
	{
		LCD_Y_Min = OSC_PIXEL_GetMin(WaveBuf[X],WaveBuf[X-1]);//起始点亮点(极限点为0或200)
		LCD_Y_Num = OSC_PIXEL_GetNum(WaveBuf[X],WaveBuf[X-1]);//点亮数目(最多200个bit|25byte)
		
		for(y = LCD_Y_Min; y < LCD_Y_Min + LCD_Y_Num; y++){
			WaveBufBit[y][X] = List_c[OSC_M_WAVE].Point;
		}
	}
	
//	LCD_Time1 = OSTime - LCD_Time1;
//	LCD_Sum[0] += LCD_Time1;
//	LCD_Cnt[0] ++;
//	if(LCD_Cnt[0] >= 10){
//		OSC_Log("LCD_Time1:%d",(uint16_t)(1.0*LCD_Sum[0]/LCD_Cnt[0]));
//		LCD_Cnt[0] = 0;
//		LCD_Sum[0] = 0;
//	}
//	
//	//统计运行时间2
//	LCD_Time2 = OSTime;
//	//刷新buf区
	LCD_FillColor_Buf(X0,Y0,X1-1,Y1-1,&WaveBufBit[0][0]);
//	LCD_Time2 = OSTime - LCD_Time2;
//	LCD_Sum[1] += LCD_Time2;
//	LCD_Cnt[1] ++;
//	if(LCD_Cnt[1] >= 10){
//		OSC_Log("LCD_Time2:%d",LCD_Sum[1]/LCD_Cnt[1]);
//		LCD_Cnt[1] = 0;
//		LCD_Sum[1] = 0;
//	}
}

如下提供了两种lcd_buf刷新函数供各位参考。

/**************************************************************
函数名称 : LCD_WriteRAM_Prepare
函数功能 : 使能写入数据到RAM
**************************************************************/

/**************************************************************
函数名称 : LCD_SetCursor
函数功能 : 设置坐标
输入参数 : x1,y1:起始地址,x2,y2:终点地址
**************************************************************/

//在指定区域内填充颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)   
//color:要填充的颜色buf|方向是从左到右从上到下
void LCD_FillColor_Buf(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *color)
{
	uint16_t i,j;
	
    LCD_SetCursor(x1,y1,x2,y2);
    LCD_WriteRAM_Prepare();
	
	for(j = 0; j < (y2-y1+1); j++)
	for(i = 0; i < (x2-x1+1); i++)
	LCD_WR_DATA( *color++ );
}
void LCD_FillColor_Bit(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *color)
{
	uint16_t i,j,bit = 0;
	
    LCD_SetCursor(x1,y1,x2,y2);
    LCD_WriteRAM_Prepare();
	
	for(j = 0; j < (y2-y1+1); j++)
	for(i = 0; i < (x2-x1+1); i++){
		if(*color & (1<<bit) )
			LCD_WR_DATA( POINT_COLOR );
		else
			LCD_WR_DATA( BACK_COLOR );
		bit++;
		if(bit>=8){
			color++;
			bit=0;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_38743772/article/details/127142750