三、简易示波器功能的实现
1.峰值检测
通过查找ad采集的数据内的最大值和最小值,然后相减即得峰峰值。
- float Get_Vpp(float arr[])
- {
- uint16_t i;
- float MAX=0,MIN=3500,Vpp=0;
- for(i=0;i<Ns;i++) // 扫描ADC数组,获取最大值和最小值
- {
- if(arr[i]>MAX)
- MAX=arr[i];
- if(arr[i]<MIN)
- MIN=arr[i];
- }
- Vpp=MAX-MIN;
- return Vpp;
- }
复制代码
2.频率检测
通过FFT变换,FFT变换的数据需要两部分,实部和虚部,由于变换的是数据是AD采集的实数据,所以只需将采集的值存入实部,虚部存入零即可。通过变换将时域信号转换到频域,然后通过取模排序,然后计算即可得到频率。他的基本思想是把原始的 N 点序列,依次分解成一系列的短序列。充分利用 DFT 计算式中指数因子所具有的对称性质和周期性质,进而求出这些短序列相应的 DFT 并进行适当组合,达到删除重复计算,减少乘法运算和简化结构的目的。当N是素数时,可以将 DFT算转化为求循环卷积,从而更进一步减少乘法次数,提高速度。
(1)FFT变换函数
- oid fft(const float real_in[], const float imag_in[], float real_out[], float imag_out[], const int n, int isign) {
- if (isign != 1 && isign != -1) {//isign=1,正变换;isign=-1,逆变换
- return;
- }
- const int Lv = mylog(n, 2);//蝶形级数
- int L;//蝶形运算级数,用于循环
- int N;//蝶形运算数据量,用于循环
- int distance;//蝶形运算两节点间的距离,用于循环(distance=N/2)
- int group;//蝶形运算的组数
- float tmpr1, tmpi1, tmpr2, tmpi2;//临时变量
- int i, j, k;
- for (i = 0; i < n; i++) {//数位倒读
- j = rev(i, Lv);
- real_out[j] = real_in[i];
- imag_out[j] = imag_in[i];
- }
- L = 1;
- distance = 1;
- N = 2;
- group = n >> 1;
- for (; L <= Lv; L++) {//蝶形循环
- for (i = 0; i < group; i++) {//每级蝶形各组循环
- for (k = 0; k < distance; k++) {//每组蝶形运算
- float theta = -2 * PI * k / N * isign;//旋转因子,逆变换的角度与正变换相反
- tmpr1 = real_out[N * i + k];
- tmpi1 = imag_out[N * i + k];//X(k)
- tmpr2 = mycos(theta) * real_out[N * i + k + distance] - mysin(theta) * imag_out[N * i + k + distance];
- tmpi2 = mycos(theta) * imag_out[N * i + k + distance] + mysin(theta) * real_out[N * i + k + distance];//WN(k)*X(k+N/2)
- real_out[N * i + k] = tmpr1 + tmpr2;
- imag_out[N * i + k] = tmpi1 + tmpi2;//X(k)=X(k)+WN(K)*X(k+N/2)
- real_out[N * i + k + distance] = tmpr1 - tmpr2;
- imag_out[N * i + k + distance] = tmpi1 - tmpi2;//X(k+N/2)=X(k)-WN(K)*X(k+N/2)
- if (isign == -1) {//逆变换结果需除以N,即除以Lv次2
- real_out[N * i + k] *= 0.5;
- imag_out[N * i + k] *= 0.5;
- real_out[N * i + k + distance] *= 0.5;
- imag_out[N * i + k + distance] *= 0.5;
- }
- }
- }
- N <<= 1;
- distance <<= 1;
- group >>= 1;
- }
- }
复制代码 (2)取模运算函数- void PowerMag(void)
- {
- uint16_t i=0;
- float Y,X,Mag;
- for (i=0; i < Ns/2; i++)
- {
-
- X =((float)y2r[i])/32768* Ns;
- Y = ((float)y2i[i])/32768* Ns;
- Mag = sqrt(X*X+ Y*Y)/Ns; // 先平方和,再开方
- y2[i] = (uint32_t)(Mag*65536);
-
- }
- y2[0] = y2[0]/2; //直流
- }
复制代码(3)然后将FFT变换的幅值进行排序,同时也对他们的下标进行了排序,以便后续的计算,即除了直流信号的第一个频率点即为改信号的频率。
- void sorting (void)
- {
- uint16_t i,j;
- uint32_t temp1;
-
- for(i=0;i<Ns/2;i++) //下标赋初值
- {
- xb[i]=i;
- }
- for(j=0;j<(Ns/2-1);j++) // 冒泡排序
- {
- for(i=1;i<(Ns/2-j-1);i++) //直流项不参与排序 从第二项开始
- {
- if(y2[i]<y2[i+1])
- {
- temp1=y2[i]; //交换数据
- y2[i]=y2[i+1];
- y2[i+1]=temp1;
- temp1=xb[i]; //交换下标
- xb[i]=xb[i+1];
- xb[i+1]=temp1;
- }
- }
- }
- }
复制代码(4)通过计算即可得到频率,采样点数将采样频率进行平分,通过排序取得的幅值最大的那个点的下标进行相乘即为频率,1.47为补偿系数,因为ADS1256采集数据后有延时,导致进行FFT变换后所对应的幅值最大点的下标前移,导致计算频率时候会偏小。
3.波形显示
通过将采集的幅值进行计算,使最后的值在屏幕大小的范围内,进行描点画图。
- void drawCurve( float rawValue,uint16_t color)
- {
- uint16_t x;
- int y;
- y = (int) rawValue/30+30; //data processing code
- if(y<0 || y > 240)
- y = lastY;
- //这里之所以是120-rawValue/280,与屏幕的扫描方向有关,如果出现上下颠倒的情况,可以改成120 +
- if(firstPoint)//如果是第一次画点,则无需连线,直接描点即可
- {
- LCD_DrawPoint(0,y,color);
- lastX=0;
- lastY=y;
- firstPoint=0;
- }
- else
- {
- x=lastX+2;
- if(x<320) //不超过屏幕宽度
- {
- LCD_DrawLine(lastX,lastY,x,y,color);
- lastX=x;
- lastY=y;
- }
- else //超出屏幕宽度,清屏,从第一个点开始绘制,实现动态更新效果
- {
- //LCD_Fill(0, 0, LCD_W, LCD_H, LCD_WHITE);//清屏//清屏,白色背景
- LCD_DrawPoint(0,y,color);
- lastX=0;
- lastY=y;
- }
- }
- }
复制代码4.LCD显示
Gitee社区已有这部分源代码和说明文档,感兴趣的读者可以参考:
https://gitee.com/Lockzhiner-Ele ... 2206/samples/b4_lcd。
四、心得体会
通过OpenHarmony操作系统 + 小凌派-RK2206开发板进行项目开发,OpenHarmony的实时性好,稳定性高,瑞芯微RK2206芯片接口比较丰富,移植适配稳定性较好,整体开发进度比较顺利,开发的难度都集中在数据处理算法上。通过这一次的应用开发,整体上对OpenHarmony和国产芯片开发还是蛮认可的,是一次不错的学习体验,特此记录!