基于RP2350B实现DDS信号发生器
该项目使用了RP2350B和综合扩展板,实现了一个可调波形、频率、幅度、直流偏移的DDS信号发生器的设计,它的主要功能为:通过R-2R电阻网络模拟DAC输出,可以生成正弦波、方波、三角波、锯齿波和直流偏置, 采集电位计数据用于控制波形的幅值,片上AD采集电阻按键网络用于控制波形类型以及波形的频率,在OLED上面显示波形类型、波形频率以及波形赋值,同时通过SPI采集的姿态传感器数据也可以用于控制波形类型。。
标签
RP2350B
DDS信号发生器
张一西
更新2025-07-08
100

1、项目概况

本设计旨在利用RP2350B核心板+综合训练扩展板建一个DDS信号发生器,主要的需求如下:

  • 能够产生正弦波、三角波、方波、直流,可以通过核心板的拨码开关控制波形的切换
  • 产生信号的幅度0-3Vpp之间可调,可以通过电位计进行调节
  • 产生信号的频率100Hz-200KHz之间可调
  • 产生的波形、波形的幅度、波形的频率都实时显示在OLED屏幕上

根据以上需求,可以细化上述需求,主要的DDS信号发生器功能如下:

  • 通过R-2R电阻网络和控制GPIO口来模拟DAC输出不同的电压,从而生成不同的波形,
  • 通过SPI读出ADS7868 AD采样器,从而可以读出电位计数据,结合波形生成策略,可控制波形赋值
  • 核心板AD采样按键电阻网络,从而判断按键的类型。
  • 根据按键的类型可以切换波形,也可以调节波形的频率
  • 通过SPI控制OLED显示波形的类型、赋值和频率。
  • IIC读取姿态传感器数据,可用于控制波形的切换。
  • 数码管可用于显示波形类型编码
  • LED灯指示按键的按下

主要使用的RP2350B核心板和综合训练扩展板如下:

RP2350 - 正反图(1000px).png

Training扩展板图(1000px).png

2、项目总体方案框图

项目总体框图如下:

DDS信号发生器总体框图2.png

根据细化的需求,拆分相关的模块如下:

  • adc:采集电阻按键网站的电压值,用于检查哪个按键或者拨码开关按下
  • ads7868:通过spi采集电位计数据,用于模块波形幅值
  • angle:iic读取姿态传感器数据,用于波形类型控制
  • blink:led闪烁,系统运行指示灯
  • dac:R-2R电阻网络电压输出,用于生成波形
  • key:对adc采集的电压进行滤波以及按键值的检查
  • oled:spi 协议控制波形、幅值等数据显示
  • multi_core:core1处理函数,接收core的数据,用于控制dac数据输出,电位计控制幅值输出,以及频率的控制
  • segment_scan:利用74hc595 串转并显示对应的数据,显示波形编码值
  • main:core0上运行,用于处理电位计、姿态传感器数据、按键逻辑控制、OLED显示以及core1的通信控制数据传输。

3、硬件使用介绍

使用到的硬件主要有如下图所示:

DDS信号发生器硬件.png

  • OLED:128*64 液晶显示屏
  • 数码管:IO驱动数码管和74HC595串转并驱动数码管
  • 74HC595:串转并驱动芯片
  • 按键:拨码以及点击按键
  • ads7868:ad采样芯片,spi协议数据读出
  • R-2R电阻网络以及TLV271:运算放大器,
  • KXTJ3-1057:姿态传感器,IIC协议总线,x,y,z三轴数据
  • LED灯:数量为8个


3.1、R-2R电阻网络DAC

  • R-2R无需与并行数据同步的时钟,直接IO口并行数据输出到10个电阻节点即可
  • 基准电压为3.3V,因此在R-2R上得到的模拟波形的峰峰值也就是3.3Vpp
  • 10个IO口,从DA0-DA9,可看出10位数据,数值越大,则幅值越大,基于此可调整不同的电压生成不同的波形。


R-2R电阻网络DAC电路图如下所示:

image.png


3.2、74HC595控制数码管

2个74HC595 来控制2个数码管,主要使用三个管脚,SERSRCLK,RCLK,时钟频率为40KHZ

  • SER:串行数据输入管脚,数据从SER口排着队依次进入。
  • SRCLK:移位寄存器的时钟输入
    • 数据从SER口排着队等待进入,该管脚输入低电平,则SER接收一位数据。
  • RCLK:存储计时器时钟的输入引脚
    • 把SER输入的8位数据存储起来,输入高电平,把移位寄存器中的8位数据一次性转存到存储时钟当中。


实验中有2个数码管,则需要2个74HC595芯片控制,一个控制数码管的位置,一个控制数码管的数据。

  • 首先初始化管脚状态
  • 其次把控制的数码管位置以及数码管封住成整体数据,1个16位数据
  • 接着通过sclk时钟,逐位通过SER管脚输出,则点亮单个数码管
  • 最后扫描2个数码管,通过状态机逐个数码管显示对应数据

具体电路与时序图如下

image.png


image.png

3.3、姿态传感器数据读取

KXTJ3-1057姿态传感器:使用IIC总线通信,400KHZ频率,通过读取固寄存器地址,可以分多次将数据读出。寄存器列表如下:

  • 首先写0x1B 地址,初始化传感器,主要是控制传感器位数,以及精读量程
  • 其次写0x1D地址,传感器输出数据频率
  • 最后就通过0x06/07、0x8/09、0xA/B读取接近传感器数值,可以有8/12/16位数值


image.png

image.png

image.png

image.png

image.png

3.4、ADA采样芯片数据读取

ADS7868 ADA采样芯片:spi协议读取采样数据,其时序图和电路图如下

  • 需要三个IO口,其中一个输入SD,用于读取数据,其他IO口用于输出,clk是时钟,cs是片选使能
  • cs使能的时候进行读数据
  • clk 时钟下拉时,读取sdo数据
  • 读取sdo数据时,前三个为0,需要过滤掉,数据总共8位。


image.png

image.png


3.5、8个LED控制

  • 多个IO口控制来确定不同的LED灯亮
  • 不需要的IO口需要设置高组态,不然导致其他LED灯亮

image.png

3.6、按键控制:

  • ADC 采集电压,按键按下电阻不同,则电压不同,根据电压的范围,确定哪个电阻按下
  • adc采集的值需要均值滤波,且需要消抖,不然有很大概率出现误触按下情况

image.png

4、功能模块设计与展示

4.1、任意波形生成

利用R-2R DAC可生成不同的电压幅值,如果在不同的时刻输出不同的值,则可以用于生成波形。

  • 正弦波:查表生成,正弦波属于中心对称以及轴对称,因此只需要生成1/4波形数据,然后通过基础值+逆序的方式就可以生成余下的波形。

dds_training_lut.png

uint16_t sine_wave_data[] = 
{
    0x0,   0xC,   0x19,  0x25,  0x32,  0x3E,  0x4B,  0x57,  
    0x63,  0x70,  0x7C,  0x88,  0x94,  0xA0,  0xAC,  0xB8,  
    0xC3,  0xCF,  0xDA,  0XE6,  0xF1,  0XFC,  0x107, 0x111,
    0x11C, 0x126, 0x130, 0x13A, 0x144, 0x14E, 0x157, 0x161,  
    0x16A, 0x172, 0x17B, 0x183, 0x18B, 0x193, 0x19B, 0x1A2,
    0x1A9, 0x1B0, 0x1B7, 0x1BD, 0x1C3, 0x1C9, 0x1CE, 0x1D4,
    0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1E9, 0x1ED, 0x1F0, 0x1F3,  
    0x1F6, 0x1F8, 0x1FA, 0x1FC, 0x1FD, 0x1FE, 0x1FF, 0x1FF,
};
        case SINE_WAVE_SEL:
        {
            int8_t i;
            for(i=0;i<64;i++)
            {
                dac_io_value_set((0x1FF + sine_wave_data[i])*ad_data_g/1024);
                sleep_us(fre);
            }
            for(i=63;i>=0;i--)
            {
                dac_io_value_set((0x1FF + sine_wave_data[i])*ad_data_g/1024);
                sleep_us(fre);
            }
            for(i=0;i<64;i++)
            {
                dac_io_value_set((0x1FF - sine_wave_data[i])*ad_data_g/1024);
                sleep_us(fre);
            }
            for(i=63;i>=0;i--)
            {
                dac_io_value_set((0x1FF - sine_wave_data[i])*ad_data_g/1024);
                sleep_us(fre);
            }
        }break;
  • 方波:控制DAC输出0,以及输出有电压幅值,如此循环往复可以生成方波。
        case SQUARE_WAVE_SEL:
        {
            dac_io_value_set(ad_data_g);
            sleep_us(fre);
            dac_io_value_set(0);
            sleep_us(fre);
        }break;
  • 三角波:控制DAC输出按照阶梯输出到最大值,然后再减小到0值。这里笔者的阶梯值可以根据幅值调控
        case TRIANGULAR_WAVE_SEL:
        {
            int8_t i;
            uint16_t value = 0;
            uint16_t avg = ad_data_g>>5;
            for(i=0;i<32;i++)
            {
                dac_io_value_set(value);
                sleep_us(fre);
                value+=avg;
            }
            for(i=0;i<32;i++)
            {
                value-=avg;
                dac_io_value_set(value);
                sleep_us(fre);
            }
        }break;
  • 锯齿波:相比三角波,只需要阶梯式增大即可。
        case SAWTOOTH_WAVE_SEL:
        {
            int8_t i;
            uint16_t value = 0;
            uint16_t avg = ad_data_g>>5;
            for(i=0;i<32;i++)
            {
                dac_io_value_set(value);
                sleep_us(fre);
                value+=avg;
            }
        }break;
  • 直流偏置:直接输出电压
        case DC_WAVE_SEL:
        {
            dac_io_value_set(ad_data_g);
        }break;

4.2、波形幅值与频率控制

  • 电位计由ADS7868 采样,通过SPI协议读出,这里SPI协议通过IO口模拟生成(硬件SPI 与当前口无法对应上)
  • 频率由按键控制,可以进行增减。
  • 通过拨码开关,可以控制波形的类型
  • 通过按键,可以控制波形的频率,通过控制波形数据点位之间的延时,可以控制波形的频率
  • 电位计数据通过SPI读出,用于控制波形的幅值,例如对于方波,用于控制高电平的幅值,即可输出不同幅值的方波,对于正弦波,幅值与最大值成比例,然后作为输出系数,对于三角波和锯齿波,幅值可用于控制阶梯式大小,然后就可以控制波形幅值。

波形频率与幅值控制.png

void core1_fifo_irq() {
    // Just record the latest entry
    uint32_t value;
    while (multicore_fifo_rvalid())  
    {
        value = multicore_fifo_pop_blocking();
        ad_data_g = ((value & 0xFF) << 2);
        wave_select = (value >> 8) & 0xFF;
        fre  = (value>>16);
    }
    multicore_fifo_clear_irq();
}
  • core0 输出的控制波形数据,通过fifo irq获取,共32位数据
  • 低16位中的低8位是幅值数据
  • 低16位中的高8位是波形选择数据
  • 高16位为频率控制数据

4.3、OLED波形信息显示

  • OLED显示通过SPI控制,这里同样采样IO口模拟(硬件SPI 与当前口无法对应上)
  • OLED的波形通过图案进行显示
  • core0发送给core1的波形控制数据同时显示在OLED上面。
  • 100HZ利用定时器定时刷新OLED显示数据


OLED刷新显示.png

void wave_info_show()
{
    uint8_t buf[128];
    uint32_t ad_data = ad_read_data();
    sprintf(buf,"Vol:%.2f V", (float)ad_data*3.0/254);
    show_common_string(40, 4, buf, 16);
    switch(wave_set)
    {
        case SQUARE_WAVE_SEL:
        {
            uint16_t fre = 1000000/wave_fre/2;
            if(fre>1000)
                sprintf(buf,"Fre:%.1f kHz", (float)fre/1000.0);
            else
                sprintf(buf,"Fre:%04d Hz", fre);
            OLED_DrawBMP(0,0, 40, 64, square_wave);
        }break;
        case SINE_WAVE_SEL:
        {
            uint16_t fre = 1000000/wave_fre/256;
            sprintf(buf,"Fre:%04d Hz", fre);
            OLED_DrawBMP(0,0, 40, 64, sine_wave);
        }break;
        case TRIANGULAR_WAVE_SEL:
        {
            uint16_t fre = 1000000/wave_fre/64;
            sprintf(buf,"Fre:%04d Hz", fre);
            OLED_DrawBMP(0,0, 40, 64, triangular_wave);
        }break;
        case SAWTOOTH_WAVE_SEL:
        {
            uint16_t fre = 1000000/wave_fre/32;
            sprintf(buf,"Fre:%04d Hz", fre);
            OLED_DrawBMP(0,0, 40, 64, sawtooth_wave);
        }break;
        case DC_WAVE_SEL:
        {
            sprintf(buf,"Fre:%04d Hz", 1);
            OLED_DrawBMP(0,0, 40, 64, dc_wave);
        }break;
        default:
        break;
    }
    show_common_string(40, 1, buf, 16);
    multicore_fifo_push_blocking((wave_fre<<16) | (wave_set<<8) | (ad_data));
}

5、项目实物展示

项目仓库地址为:DDS 信号发生器

项目硬件展示如下图所示:

  • 波形:正弦波
  • 幅值:1.95V
  • 频率:38HZ

image.png项目展示效果图2.jpg

  • 波形:三角波
  • 幅值:1.89V
  • 频率:156HZ

image.png

项目展示效果图8-三角波.jpg

  • 波形:锯齿波
  • 幅值:1.89V
  • 频率:312HZ


image.png

项目展示效果图10-锯齿.jpg

  • 波形:方波
  • 幅值:1.97V
  • 频率:4.95KHZ

image.png

项目展示效果图6-方波.jpg

  • 波形:直流偏置
  • 幅值:1.9V
  • 频率:0HZ

image.png

项目展示效果图3-直流.jpg



6、项目总结与展望

6.1、项目总结与展望

  • DDS任意波形发生器总体难度不是很大,核心的控制还是波形的频率控制,单片机不像FPGA那样,频率可以达到很高,且控制相对容易,单片机如果输出相对较高的IO还是相对困难的,因为需要精准的去控制频率,如果有其他程序运行,就会干扰频率范围,笔者采样core1运行DAC,没有其他程序感染,延时精准控制频率,但是频率范围有限,后续考虑DMA控制IO或者PWM控制IO输出。
  • DDS信号发生器当前功能还是相对简单,后续考虑串口接收上位机数据来进行频率生成以及显示
  • DDS信号发生器如果要生成任意波形,考虑上位机传输波形数据下来生成,定义好相关格式,比如点位数据等等,然后可以显示任意波形。

6.2 、心得与感受

  • 本次硬禾推出全新RP2350B核心板内置姿态传感器,数码管,按键以及LED灯,外设功能相对丰富,对于初学者来说,还是相对友好的,且按键以及LED灯控制,电路相对有趣,也可以学到很多基础知识。
  • RP2350B核心板相对较小,如果用来做物联网等场景的控制板,还是很有竞争力
  • 综合外设扩展板板载OLED,电位计,DAC等外设,满足对基本外设的需求,且相对较小,个人还是比较喜欢这样小巧的扩展板。

7、参考

附件下载
dds-project.zip
dds project,在blink文件夹下面有所有的文件,编译好即可运行build下面blink下面的dds.uf2
dds.uf2
RP2350B烧录文件
团队介绍
一名嵌入式工程师,励志做自己喜欢的玩具,喜欢研究芯片底层架构,编程模型,指令集等,热爱去研究使用开源软件,跑步、乒乓球爱好者,个人网站一西站点:http://zyixi.xyz/
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号