Waveform Refresh Scheme

Table of contents

foreword

1. Real-time refresh and trigger mode refresh

1.1 Real-time refresh

1.2 Trigger mode refresh

2. Use cache to refresh LCD


foreword

This article follows the previous "STM32 Oscilloscope Design" to discuss specific waveform refresh.

The main content is the following three points:

1. Real-time refresh

2. Trigger mode refresh

3. Use cache to refresh LCD


1. Real-time refresh and trigger mode refresh

Both real-time refresh and trigger mode refresh are classified from the perspective of data processing.

Note that in this article, in any case, the next ADC acquisition transmission is started before the waveform is refreshed .

Prerequisite concepts and parameters:

Sampling point: every sampled data is a sampling point

Waveform point: A waveform point is composed of several sampling points, which can be one or multiple to take the average.

Waveform record length: the number of waveform points in a piece of waveform data is the waveform record length

For example, my hardware environment here is:

1、

Single-channel ADC+240*320 screen (waveform display area is 200*300),

Then, a waveform point is a sampling point, and the waveform record length is 300.

2、

The LCD write timing is set from left to right, from top to bottom, it takes 28ms to write 240*320 (full screen), and 22ms to write 200*300 (waveform display area).

The X\Y axis direction is 25 as a small grid, 8 grids horizontally and 12 grids vertically.

1.1 Real-time refresh

Whether it can be refreshed in real time depends on the screen refresh rate, that is, it takes time to refresh the screen.

It takes 22ms to refresh here (fixed 22ms, the implementation method is as follows), and it can be refreshed in real time as long as it takes more than 22ms to collect a waveform.

(Why is it greater than? When the sampling time is less than 22ms, when the refresh is performed at the end of each sampling, a part of the cycle is missed in this refresh time. When the sampling time is greater than 22ms, since the ADC acquisition transmission can be synchronized with the screen refresh using DMA , the acquisition of the ADC will not be delayed when the screen is refreshed)

The corresponding time base gear is, 22*1000/300us=each waveform point 73us, one grid (25 points) 1833us, close to 2ms,

Then take 2ms per grid as the dividing line here .

In the gear of 2ms and above, start the next ADC acquisition transmission before the waveform refresh , and wait for the ADC acquisition to complete after the refresh, so as to ensure a fixed refresh frequency.

The ideal situation is to maximize the acquisition time until it is close to the refresh time, so that every time a piece of data is refreshed, the next ADC data is ready.

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

For the real-time mode refresh of single-channel ADC acquisition, the OSC_DEPTH storage depth can be set to 300, that is, 300 points are collected and 300 points are refreshed.

Either way, there must be a WaveBuf in the code to cache the normalized waveform data (the length of the array is the width of the waveform area) for LCD refresh.

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 Trigger mode refresh

For more than 2ms, it can be refreshed in real-time mode. When it is below 2ms, the trigger mode is generally used to refresh.

In trigger mode: OSC_DEPTH The storage depth can be set to an integer multiple of 300, and the length is arbitrary when it exceeds 1K. For example, take 600 here. Other ADC parameters are still the same.

The difference is the data processing method. We generally judge the rising edge or falling edge by software and record (the frequent interruption caused by the hardware method will affect the processing speed and refresh speed, which can only be applied to low-frequency signals).

The following code,

Processing logic: first find the position A of the first falling edge, and if found, take 300 points from position A to refresh the calculation. If not found, take the first 300 point data to refresh.

Note: The starting index during processing is not 0, because in the original ADC acquisition, there is always a large deviation in the first 1 or 2 data, and 10 is directly taken here.

//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[]);

Either way, the same API is called to refresh the screen, and the outside only needs to calculate the normalized data.

2. Use cache to refresh LCD

Here, an array of size 300*200*2 is directly used as a cache. You can design your own algorithm to save memory or use external storage.

The specific process is as follows:

1. The first step is to clear 0

2. Calculate the grid points first, and assign colors to the grid points

3. The third step is to calculate the absolute value of the Y coordinate difference and the minimum Y coordinate of each two points according to the X and Y value coordinates, and then calculate how many points need to be drawn on the X axis (assign colors)

4. Send data to lcd_buf refresh function to refresh

//波形显示区域|利用宏定义进行处理|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;
//	}
}

Two lcd_buf refresh functions are provided as follows for your reference.

/**************************************************************
函数名称 : 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;
		}
	}
}

Guess you like

Origin blog.csdn.net/weixin_38743772/article/details/127142750