基于OpenHarmony+小凌派RK2206开发板制作的简易示波器

描述

基于OpenHarmony + 小凌派RK2206开发板制作的简易示波器,实时采集波形,实时计算并实时显示对应的波形。OpenHarmony实时性较高,稳定性好,瑞芯微RK2206芯片接口丰富,OpenHarmony芯片适配稳定性好,做出来的简易示波器效果还不错。本文先做第一期的技术文档,后续将持续完善功能和技术文档更新。

 

1

项目简介

 

本文基于OpenHarmony操作系统 + 小凌派RK2206开发板而做的简易示波器开发。
 

本开发以瑞芯微RK2206芯片 + OpenHarmony 3.0LTS操作系统 + 小凌派-RK2206开发板为基础,以模数转换芯片ADS1256为采集数据芯片实时采集,然后通过峰值检测和fft变换算法处理采集数据,最后将处理完成的数据输送到LCD液晶屏上实时显示。

 

项目实验视频如下所示:

 

 

2

模块介绍

 

1.ADS1256模块
ADS1256是一款 24bit ADC转换模块。ADS1256可以通过SPI进行访问 的高精度的转换器。

 

示波器

 

上图为ADS1256芯片结构和引脚图,从图可以看出ADS1256的通道资源比较丰富,可以配置成8个单端ADC通道,也可以配置成4个差分通道。ADS1256的通信接口为串行接口,同时还有4个通用的IO口,不过这四个IO口不经常用到。

 

ADS1256内部有许多寄存器需要配置,相较于ADS1232用起来要复杂的多。

 

下面介绍一下ADS1256的初始化。

 

通过SPI通信配置ADS1256的参数,增益以及转换速率。

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void ADS1256_CfgADC(uint8_t gain,uint8_t drate)   // 初始化设置,设置增益以及转换速率{    uint8_t buf[4];    ADS1256_WriteCmd(CMD_RESET);                // 写复位指令    ADS1256_WriteReg(REG_STATUS,0XF4);          // 写状态,数据传输默认高位在前,启动矫正,禁止使用缓冲    ADS1256_WriteCmd(CMD_SELFCAL);              // 自校准    ToyUdelay(20);    {        buf[0] = (0 << 3) | (1 << 2) | (0 << 1);        buf[1] = 0x08;                      //通道设置选择        buf[2] = (0 << 5) | (0 << 3) | (gain << 0);        buf[3] = drate; // DRATE_10SPS; /* 选择数据输出速率 */        CS_L;        ADS1256_Send8Bit(CMD_WREG|0);          // 写寄存器        ADS1256_Send8Bit(0x03);                // 连续写入4个数据        ADS1256_Send8Bit(buf[0]);              // 设置状态寄存器        ADS1256_Send8Bit(buf[1]);              // 设置输入通道参数        ADS1256_Send8Bit(buf[2]);              // 设置ADCON控制寄存器,增益        ADS1256_Send8Bit(buf[3]);                // 设置数据速率        CS_H;    }    ToyUdelay(50);}

(左右移动查看全部内容)

 

然后选择通道0,等待数据转换完成后获取他采集的AD值

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
uint32_t ADS1256_GetAdc(uint8_t channel){    uint32_t read;    uint16_t val = 0;    read = 0;//  while(DRDY);           //当DRDY变为低电平时,数据开始传输    ADS1256_WriteReg(REG_MUX,channel);       // 写入读取的通道    ADS1256_WriteCmd(CMD_SYNC);              //同步A/D转换命令//  Delay_1us(1);    ADS1256_WriteCmd(CMD_WAKEUP);            //完成SYNC并退出待机模式    GpioGetVal(DRDY, &val);    while(val==1)                             // 等待数据转换完成    {        GpioGetVal(DRDY, &val);    }    CS_L;                                    //片选拉低    ADS1256_Send8Bit(CMD_RDATA);             //读取数据命令    //连续接收3个数据,高字节在前    read = ((uint32_t)ADS1256_Recive8Bit() << 16);             read +=( (uint32_t)ADS1256_Recive8Bit() << 8);    read += ADS1256_Recive8Bit() ;    CS_H;    return read;   }

(左右移动查看全部内容)

 

2.LCD液晶屏模块

本项目使用的是ST7789V, 用于单片驱动262K色图像TFT-LCD, 包含 720(240*3色) x 320 线输出, 可以直接以SPI协议, 或者8位/9位/16位/18位并行连接外部控制器。ST7789V显示数据存储在片内240x320x18 bits内存中, 显示内存的读写不需要外部时钟驱动。

 

具体接线如下图所示:

 

示波器

 

其中,LCD液晶屏引脚功能描述,如下表5.3.1所示。

 

表5.3.1 LCD液晶屏引脚功能表:

 

示波器

 

其中,LCD液晶屏与小凌派-RK2206开发板连接如下图所示:

示波器

3

简易示波器功能的实现

 

1.峰值检测

通过查找ad采集的数据内的最大值和最小值,然后相减即得峰峰值。

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
float Get_Vpp(float arr[]){        uint16_t i;        float MAX=0,MIN=3500,Vpp=0;        for(i=0;i""    ="" 扫描adc数组,获取最大值和最小值        {            if(arr[i]>MAX)                         MAX=arr[i];            if(arr[i]                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;i2;i++) =""   =""  ="" 下标赋初值    {        xb[i]=i;    }    for(j=0;j<(Ns/2-1);j++)                 // 冒泡排序     {        for(i=1;i<(Ns/2-j-1);i++)       //直流项不参与排序  从第二项开始                      {            if(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变换后所对应的幅值最大点的下标前移,导致计算频率时候会偏小。

  •  
fre=Fs*xb[1]*1.47/Ns;

(左右移动查看全部内容)

 

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-Electronics/lockzhiner-rk2206-openharmony3.0lts/tree/master/vendor/lockzhiner/rk2206/samples/b4_lcd

 

4

心得体会

 

通过OpenHarmony操作系统 + 小凌派-RK2206开发板进行项目开发,OpenHarmony的实时性好,稳定性高,瑞芯微RK2206芯片接口比较丰富,移植适配稳定性较好,整体开发进度比较顺利,开发的难度都集中在数据处理算法上。通过这一次的应用开发,整体上对OpenHarmony和国产芯片开发还是蛮认可的,是一次不错的学习体验,特此记录!

 

原文标题:基于OpenHarmony操作系统的简易示波器开发心得

文章出处:【微信公众号:HarmonyOS官方合作社区】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分