STM32H723ZGT6-实用信号源的设计和制作-HAL

任务描述

一、任务
在给定±15V 电源电压条件下,设计并制作一个正弦波和脉冲波信号源。
二、要求
1.基本要求
(1)正弦波信号源
① 信号频率: 20Hz~20kHz 步进调整,步长为20Hz
② 频率稳定度:优于 10-3
③ 波形无明显失真
(2)脉冲波信号源
① 信号频率: 20Hz~20kHz 步进调整,步长为20Hz
② 上升时间和下降时间: ≤2μs
③ 平顶斜降: ≤5%
④ 脉冲占空比: 2%~98%步进可调,步长为 2%
(3)上述两个信号源公共要求
① 频率可预置。
② 在负载为 600Ω 时,输出幅度为 3V。
③ 完成 5 位频率的数字显示。
2.发挥部分
(1)正弦波和脉冲波频率步长改为 5Hz。
(2)正弦波和脉冲波幅度可步进调整,调整范围为 100mV~5V,步长为 50mV。
(3)正弦波和脉冲波频率可自动步进,步长为 5Hz。
(4)降低正弦波非线性失真系数及其他。
三、说明
搭建单片机小系统,至少包含显示模块(至少为 128x64 以上像素)、人机交互模块(至少
为 4x4 按键或触摸屏)。

软件方案

使用AD9910模块的RAM模式实现PWM波和正弦波的输出,为了使AD9910输出信号的频率更加精确,采用了Si5351A时钟信号发生器模块来作为AD9910的外部时钟源。

配置CubeMX

主要是AD9910模块和Si5351A模块通信引脚的配置。此外,使用串口通信。

串口通信要实现串口+DMA+中断接收,串口的配置如下

串口中断优先级的设置可以在System Core的NVIC中进行设置

一般默认把system tick timer的优先级设置为0

在CubeMX中把堆栈的最小值设置为0x2000,因为在工程中有一个循环生成波表,如果堆栈区小的话,会进入HardFault_Handler这个硬件异常状态

CubeMX配置完毕生成代码即可。

CubeMX的配置文件,可以在用CubeMX新建一个工程后使用Import Project导入,之后只需要把时钟修改即可。

CubeMX配置完毕

代码编写

1.串口重定向,看之前的文章进行串口重定向,记得勾选Micro LIB,不然就下载进单片机后没法正常运行,而在Keil的调试中确实正常的。

AD9910.c

以下是我根据资料自己写的函数

// 自己写的函数

/************************************************************
** 函数名称 :void Init_AD9910_Fre(uint32_t Frequency)
** 函数功能 :根据Frequency来选择AD9910合适的VOC进行初始化,即改变cfr3[0]的值
**************************************************************/
void Init_AD9910_Fre(uint32_t Frequency)
{
    int32_t CLK_Frequency = Frequency;
    if (CLK_Frequency >= 400000000 && CLK_Frequency <= 460000000)
    {
        cfr3[0] = 0x00;
    }
    else if (CLK_Frequency >= 455000000 && CLK_Frequency <= 530000000)
    {
        cfr3[0] = 0x01;
    }
    else if (CLK_Frequency >= 530000000 && CLK_Frequency <= 615000000)
    {
        cfr3[0] = 0x02;
    }
    else if (CLK_Frequency >= 650000000 && CLK_Frequency <= 790000000)
    {
        cfr3[0] = 0x03;
    }
    else if (CLK_Frequency >= 760000000 && CLK_Frequency <= 875000000)
    {
        cfr3[0] = 0x04;
    }
    else if (CLK_Frequency >= 920000000 && CLK_Frequency <= 1030000000)
    {
        cfr3[0] = 0x05;
    }

    uchar k, m;
    //    AD9110_IOInit(); // IO初始化
    // AD9910_PWR = 0;  // 软件拉低
    HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, GPIO_PIN_RESET);
    // PROFILE2 = 0;
    HAL_GPIO_WritePin(PF2_GPIO_Port, PF2_Pin, GPIO_PIN_RESET);
    // PROFILE1 = 0;
    HAL_GPIO_WritePin(PF1_GPIO_Port, PF1_Pin, GPIO_PIN_RESET);
    // PROFILE0 = 0;
    HAL_GPIO_WritePin(PF0_GPIO_Port, PF0_Pin, GPIO_PIN_RESET);
    // DRCTL = 0;
    HAL_GPIO_WritePin(DRC_GPIO_Port, DRC_Pin, GPIO_PIN_RESET);
    // DRHOLD = 0;
    HAL_GPIO_WritePin(DRO_GPIO_Port, DRO_Pin, GPIO_PIN_RESET);
    // MAS_REST = 1;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
    HAL_Delay(5);
    // MAS_REST = 0;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 发送CFR1控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr1[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x01); // 发送CFR2控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr2[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x02); // 发送CFR3控制字地址
    if (Frequency * 2000)

        for (m = 0; m < 4; m++)
            txd_8bit(cfr3[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;

    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);
}
/************************************************************
** 函数名称 :Reset_AD9910()
** 函数功能 :复位AD9910
**************************************************************/
void Reset_AD9910()
{
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
}

/************************************************************
** 函数名称 :PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty)
** 函数功能 :根据地址步进率,电压,占空比来产生合适的PWM
更改地址步进率是更改RAM_Profile0[1]和RAM_Profile0[2]
**************************************************************/
void PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty)
{
    int i;
    const uint16_t LENGTH = 100;
    uint32_t PWM_Wave[LENGTH];
    uint8_t CFR1[] = {0x40, 0x40, 0x00, 0x00}; // RAM回放目的:幅度;;开启AD9910反Sinc滤波
    // RAM_Profile0[1](高8位)  与  RAM_Profile0[2](低8位)共16位控制字M,决定了输出波形频率, 频率 = Fsysclk / (4*M) / 输出点数 = 1000000000 / (4*M) / 1024
    uint8_t RAM_Profile0[] = {0x00, 0x00, 0x7A, 0xff, 0xc0, 0x00, 0x00, 0x04}; //	地址步进率0XFFFF=65535,,从地址0回放到1023,共1024点,输出频率 = 1G / (4*122) /1024 =2000Hz  // 连续循环模式RAM_PROx[7]=0x04
	// 将 step_rates 的高位字节存储到 RAM_Profile0[1]
	// 使用右移 8 位操作获取高位字节,然后使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[1] = (uint8_t)(step_rates >> 8) & 0xFF;

	// 将 step_rates 的低位字节存储到 RAM_Profile0[2]
	// 使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[2] = (uint8_t)step_rates & 0xFF;

	// 将 (LENGTH - 1) 的高位 6 位存储到 RAM_Profile0[3]
	// 使用右移 2 位操作获取高位 6 位
	RAM_Profile0[3] = (uint8_t)((LENGTH - 1) >> 2);

	// 将 (LENGTH - 1) 的低位 2 位存储到 RAM_Profile0[4]
	// 使用左移 6 位操作获取低位 2 位
	RAM_Profile0[4] = (uint8_t)((LENGTH - 1) << 6);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 将CFR1写入其寄存器0x00
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0X0E); // 写寄存器RAM_Profile0
    for (i = 0; i < 8; i++)
    {
        txd_8bit(RAM_Profile0[i]); // 将RAM的起始和终止地址、地址步进率写入相应的寄存器  0x0e
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x16);
    uint16_t Zero_Num = LENGTH * Duty / 100;
    for (i = 0; i < Zero_Num; i++)
    {
        PWM_Wave[i] = 0;
    }
    for (i = Zero_Num; i < LENGTH; i++)
    {
        PWM_Wave[i] = voltage;
    }
    for (i = 0; i < LENGTH; i++)
    {
        Write_32bit(PWM_Wave[i] << 18); // 将三角波(或其他任意波形)的数据数组写入ram,32位寄存器,14位DAC值,数据左对齐,故左移18位
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00);  // 将CFR1写入其地址0x00
    CFR1[0] |= 0X80; // 使能RAM
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);

    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
}
/************************************************************
** 函数名称 :Sin_Generate(uint16_t step_rates, uint16_t voltage)
** 函数功能 :根据地址步进率,电压,来产生合适的正弦波
更改地址步进率是更改RAM_Profile0[1]和RAM_Profile0[2]
**************************************************************/
void Sin_Generate(uint16_t step_rates, uint16_t voltage)
{
    int i;
    const uint16_t LENGTH = 100;
    uint32_t Sin_Wave[LENGTH];
    uint8_t CFR1[] = {0x40, 0x40, 0x00, 0x00};                                 // RAM回放目的:幅度;;开启AD9910反Sinc滤波
    uint8_t RAM_Profile0[] = {0x00, 0x00, 0x7A, 0xff, 0xc0, 0x00, 0x00, 0x04}; //	地址步进率0XFFFF=65535,,从地址0回放到1023,共1024点,输出频率 = 1G / (4*122) /1024 =2000Hz  // 连续循环模式RAM_PROx[7]=0x04
	// 将 step_rates 的高位字节存储到 RAM_Profile0[1]
	// 使用右移 8 位操作获取高位字节,然后使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[1] = (uint8_t)(step_rates >> 8) & 0xFF;

	// 将 step_rates 的低位字节存储到 RAM_Profile0[2]
	// 使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[2] = (uint8_t)step_rates & 0xFF;

	// 将 (LENGTH - 1) 的高位 6 位存储到 RAM_Profile0[3]
	// 使用右移 2 位操作获取高位 6 位
	RAM_Profile0[3] = (uint8_t)((LENGTH - 1) >> 2);

	// 将 (LENGTH - 1) 的低位 2 位存储到 RAM_Profile0[4]
	// 使用左移 6 位操作获取低位 2 位
	RAM_Profile0[4] = (uint8_t)((LENGTH - 1) << 6);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 将CFR1写入其寄存器0x00
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0X0E); // 写寄存器RAM_Profile0
    for (i = 0; i < 8; i++)
    {
        txd_8bit(RAM_Profile0[i]); // 将RAM的起始和终止地址、地址步进率写入相应的寄存器  0x0e
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x16);
    for (i = 0; i < LENGTH; i++)
    {
        Sin_Wave[i] = (float)voltage/2 * sin(2 * PI / 100 * i)+(float)voltage/2;
    }
    for (i = 0; i < LENGTH; i++)
    {
        Write_32bit(Sin_Wave[i] << 18); // 将三角波(或其他任意波形)的数据数组写入ram,32位寄存器,14位DAC值,数据左对齐,故左移18位
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00);  // 将CFR1写入其地址0x00
    CFR1[0] |= 0X80; // 使能RAM
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);

    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
}

根据频率对AD9910进行初始化需要依据AD9910数据手册中VCO的范围

信号源输入信号给AD9910之后,会进行25倍频,VCO的范围选择依据是倍频后的频率。

产生PWM和正弦波需要修改AD9910的RAM_Profile0,通过修改数组RAM_Profile0[]来实现

RAM_Profile0[1]和RAM_Profile0[2]这两个值是控制步进率

RAM_Profile0[3]和RAM_Profile0[4]这两个值是控制信号长度,在该工程中信号长度为100

下面说明了如何更改这些值

 // 将 step_rates 的高位字节存储到 RAM_Profile0[1]

 // 使用右移 8 位操作获取高位字节,然后使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
    RAM_Profile0[1] = (uint8_t)(step_rates >> 8) & 0xFF;

// 将 step_rates 的低位字节存储到 RAM_Profile0[2]

 // 使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
    RAM_Profile0[2] = (uint8_t)step_rates & 0xFF;

// 将 (LENGTH - 1) 的高位 6 位存储到 RAM_Profile0[3]
// 使用右移 2 位操作获取高位 6 位
    RAM_Profile0[3] = (uint8_t)((LENGTH - 1) >> 2);

// 将 (LENGTH - 1) 的低位 2 位存储到 RAM_Profile0[4]
// 使用左移 6 位操作获取低位 2 位
    RAM_Profile0[4] = (uint8_t)((LENGTH - 1) << 6);

完整的AD9910.c文件

#include "stm32h7xx_hal.h"
#include "AD9910.h"
#include "math.h"
#define PI 3.141592654
uchar cfr1[] = {0x00, 0x40, 0x00, 0x00};                              // cfr1控制字
const uchar cfr2[] = {0x01, 0x00, 0x00, 0x00};                        // cfr2控制字
uchar cfr3[] = {0x00, 0x0F, 0x41, 0x32};                              // cfr3控制字  40M输入  25倍频  VC0=101   ICP=001;{0000 0101, 0000 1111, 0100 0001, 0011 0010}
uchar profile11[] = {0x3f, 0xff, 0x00, 0x00, 0x25, 0x09, 0x7b, 0x42}; // profile1控制字 0x25,0x09,0x7b,0x42
uint32_t sin_wave[] = {
    8192, 8706, 9218, 9726, 10229, 10723, 11207, 11679, 12138, 12581, 13006, 13413,
    13799, 14163, 14503, 14819, 15108, 15370, 15603, 15808, 15982, 16126, 16238, 16318,
    16367, 16383, 16367, 16318, 16238, 16126, 15982, 15808, 15603, 15370, 15108, 14819,
    14503, 14163, 13799, 13413, 13006, 12581, 12138, 11679, 11207, 10723, 10229, 9726,
    9218, 8706, 8192, 7677, 7165, 6657, 6154, 5660, 5176, 4704, 4245, 3802,
    3377, 2970, 2584, 2220, 1880, 1564, 1275, 1013, 780, 575, 401, 257,
    145, 65, 16, 0, 16, 65, 145, 257, 401, 575, 780, 1013,
    1275, 1564, 1880, 2220, 2584, 2970, 3377, 3802, 4245, 4704, 5176, 5660,
    6154, 6657, 7165, 7677};

// 01振幅控制 23相位控制 4567频率调谐字

/************************************************************
** 函数名称 :void AD9110_IOInit(void)
** 函数功能 :控制AD9910需要用到的IO口在此初始化
** 入口参数 :无
** 出口参数 :无
** 函数说明 :无
**************************************************************/
// void AD9110_IOInit(void)
//{
//     GPIO_InitTypeDef GPIO_InitStructure;

//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); // 使能PB,PE端口时钟

//    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10 | GPIO_Pin_1 | GPIO_Pin_0;
//    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//    GPIO_Init(GPIOB, &GPIO_InitStructure);

//    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4 | GPIO_Pin_3 | GPIO_Pin_2 | GPIO_Pin_1 | GPIO_Pin_0;
//    GPIO_Init(GPIOA, &GPIO_InitStructure);

//    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
//    GPIO_Init(GPIOC, &GPIO_InitStructure);
//}

/************************************************************
** 函数名称 :void txd_8bit(uchar txdat)
** 函数功能 :AD9910串行口写入数据
** 入口参数 :8位寄存器数据
** 出口参数 :无
** 函数说明 :无
**************************************************************/
void txd_8bit(uchar txdat)
{
    uchar i, sbt;
    sbt = 0x80;
    // SCLK = 0;
    HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    for (i = 0; i < 8; i++)
    {
        if ((txdat & sbt) == 0)
            // AD9910_SDIO = 0;
            HAL_GPIO_WritePin(SDIO_GPIO_Port, SDIO_Pin, GPIO_PIN_RESET);
        else
            // AD9910_SDIO = 1;
            HAL_GPIO_WritePin(SDIO_GPIO_Port, SDIO_Pin, GPIO_PIN_SET);
        // SCLK = 1;
        HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
        sbt = sbt >> 1;
        // SCLK = 0;
        HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    }
}

/************************************************************
** 函数名称 :void Write_32bit(uint32_t dat)
** 函数功能 :AD9910串行口写入数据
** 入口参数 :32位寄存器数据
** 出口参数 :无
** 函数说明 :无
**************************************************************/
void Write_32bit(uint32_t dat)
{
    uint8_t i;
    uint32_t com;
    com = 0x80000000;
    // SCLK = 0;
    HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    for (i = 0; i < 32; i++)
    {
        if ((dat & com) == 0)
            // AD9910_SDIO = 0;
            HAL_GPIO_WritePin(SDIO_GPIO_Port, SDIO_Pin, GPIO_PIN_RESET);
        else
            // AD9910_SDIO = 1;
            HAL_GPIO_WritePin(SDIO_GPIO_Port, SDIO_Pin, GPIO_PIN_SET);
        // SCLK = 1;
        HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
        com = com >> 1;
        // SCLK = 0;
        HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    }
}

/************************************************************
** 函数名称 :void Init_ad9910(void))
** 函数功能 :初始化AD9910的管脚和最简单的内部寄存器的配置,
** 入口参数 :无
** 出口参数 :无
** 函数说明 :无
**************************************************************/
void Init_AD9910(void)
{
    uchar k, m;

    //    AD9110_IOInit(); // IO初始化
    // AD9910_PWR = 0;  // 软件拉低
    HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, GPIO_PIN_RESET);
    // PROFILE2 = 0;
    HAL_GPIO_WritePin(PF2_GPIO_Port, PF2_Pin, GPIO_PIN_RESET);
    // PROFILE1 = 0;
    HAL_GPIO_WritePin(PF1_GPIO_Port, PF1_Pin, GPIO_PIN_RESET);
    // PROFILE0 = 0;
    HAL_GPIO_WritePin(PF0_GPIO_Port, PF0_Pin, GPIO_PIN_RESET);
    // DRCTL = 0;
    HAL_GPIO_WritePin(DRC_GPIO_Port, DRC_Pin, GPIO_PIN_RESET);
    // DRHOLD = 0;
    HAL_GPIO_WritePin(DRO_GPIO_Port, DRO_Pin, GPIO_PIN_RESET);
    // MAS_REST = 1;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
    HAL_Delay(5);
    // MAS_REST = 0;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 发送CFR1控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr1[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x01); // 发送CFR2控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr2[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;

    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x02); // 发送CFR3控制字地址

    for (m = 0; m < 4; m++)
        txd_8bit(cfr3[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;

    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);
}
/************************************************************
** 函数名称 :void Txfrc(void))
** 函数功能 :向AD9910芯片发送频率,幅度等相关控制数据
** 入口参数 :无
** 出口参数 :无
** 函数说明 :无
**************************************************************/
void Txfrc(void)
{
    uchar m;

    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x0e); // 发送profile1控制字地址
    for (m = 0; m < 8; m++)
        txd_8bit(profile11[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
}

/************************************************************
** 函数名称 :void AD9910_FreWrite(void))
** 函数功能 :将需要的频率转换为对应的控制数据,保存进profile11并发送到芯片
** 入口参数 :目标频率,单位Hz,范围0~420000000
** 出口参数 :无
** 函数说明 :无
**************************************************************/
void AD9910_FreWrite(ulong Freq)
{
    ulong Temp;
    Temp = (ulong)Freq * 4.294967296; // 将输入频率因子分为四个字节  主频1GHz,32位相位累加器,故每Hz在的控制字增量 delta =  4.294967296 = (2^32)/1000000000
    profile11[7] = (uchar)Temp;
    profile11[6] = (uchar)(Temp >> 8);
    profile11[5] = (uchar)(Temp >> 16);
    profile11[4] = (uchar)(Temp >> 24);
    Txfrc();
}

/************************************************************
** 函数名称 :void AD9910_AmpWrite(void))
** 函数功能 :将幅度控制数据保存到profile11并写入芯片
** 入口参数 :幅度控制字,范围0~16383
** 出口参数 :无
** 函数说明 :14位幅度控制字,控制数据0~16383对应输出幅度0~800mV左右
**************************************************************/
void AD9910_AmpWrite(uint16_t Amp)
{
    profile11[0] = (Amp % 16384) >> 8;
    profile11[1] = (Amp % 16384) & 0xff;
    Txfrc();
}

// 自己写的函数

/************************************************************
** 函数名称 :void Init_AD9910_Fre(uint32_t Frequency)
** 函数功能 :根据Frequency来选择AD9910合适的VOC进行初始化,即改变cfr3[0]的值
**************************************************************/
void Init_AD9910_Fre(uint32_t Frequency)
{
    int32_t CLK_Frequency = Frequency;
    if (CLK_Frequency >= 400000000 && CLK_Frequency <= 460000000)
    {
        cfr3[0] = 0x00;
    }
    else if (CLK_Frequency >= 455000000 && CLK_Frequency <= 530000000)
    {
        cfr3[0] = 0x01;
    }
    else if (CLK_Frequency >= 530000000 && CLK_Frequency <= 615000000)
    {
        cfr3[0] = 0x02;
    }
    else if (CLK_Frequency >= 650000000 && CLK_Frequency <= 790000000)
    {
        cfr3[0] = 0x03;
    }
    else if (CLK_Frequency >= 760000000 && CLK_Frequency <= 875000000)
    {
        cfr3[0] = 0x04;
    }
    else if (CLK_Frequency >= 920000000 && CLK_Frequency <= 1030000000)
    {
        cfr3[0] = 0x05;
    }

    uchar k, m;
    //    AD9110_IOInit(); // IO初始化
    // AD9910_PWR = 0;  // 软件拉低
    HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, GPIO_PIN_RESET);
    // PROFILE2 = 0;
    HAL_GPIO_WritePin(PF2_GPIO_Port, PF2_Pin, GPIO_PIN_RESET);
    // PROFILE1 = 0;
    HAL_GPIO_WritePin(PF1_GPIO_Port, PF1_Pin, GPIO_PIN_RESET);
    // PROFILE0 = 0;
    HAL_GPIO_WritePin(PF0_GPIO_Port, PF0_Pin, GPIO_PIN_RESET);
    // DRCTL = 0;
    HAL_GPIO_WritePin(DRC_GPIO_Port, DRC_Pin, GPIO_PIN_RESET);
    // DRHOLD = 0;
    HAL_GPIO_WritePin(DRO_GPIO_Port, DRO_Pin, GPIO_PIN_RESET);
    // MAS_REST = 1;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
    HAL_Delay(5);
    // MAS_REST = 0;
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 发送CFR1控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr1[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x01); // 发送CFR2控制字地址
    for (m = 0; m < 4; m++)
        txd_8bit(cfr2[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x02); // 发送CFR3控制字地址
    if (Frequency * 2000)

        for (m = 0; m < 4; m++)
            txd_8bit(cfr3[m]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;

    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    for (k = 0; k < 10; k++)
        ;
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);
}
/************************************************************
** 函数名称 :Reset_AD9910()
** 函数功能 :复位AD9910
**************************************************************/
void Reset_AD9910()
{
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
}

/************************************************************
** 函数名称 :PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty)
** 函数功能 :根据地址步进率,电压,占空比来产生合适的PWM
更改地址步进率是更改RAM_Profile0[1]和RAM_Profile0[2]
**************************************************************/
void PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty)
{
    int i;
    const uint16_t LENGTH = 100;
    uint32_t PWM_Wave[LENGTH];
    uint8_t CFR1[] = {0x40, 0x40, 0x00, 0x00}; // RAM回放目的:幅度;;开启AD9910反Sinc滤波
    // RAM_Profile0[1](高8位)  与  RAM_Profile0[2](低8位)共16位控制字M,决定了输出波形频率, 频率 = Fsysclk / (4*M) / 输出点数 = 1000000000 / (4*M) / 1024
    uint8_t RAM_Profile0[] = {0x00, 0x00, 0x7A, 0xff, 0xc0, 0x00, 0x00, 0x04}; //	地址步进率0XFFFF=65535,,从地址0回放到1023,共1024点,输出频率 = 1G / (4*122) /1024 =2000Hz  // 连续循环模式RAM_PROx[7]=0x04
	// 将 step_rates 的高位字节存储到 RAM_Profile0[1]
	// 使用右移 8 位操作获取高位字节,然后使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[1] = (uint8_t)(step_rates >> 8) & 0xFF;

	// 将 step_rates 的低位字节存储到 RAM_Profile0[2]
	// 使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[2] = (uint8_t)step_rates & 0xFF;

	// 将 (LENGTH - 1) 的高位 6 位存储到 RAM_Profile0[3]
	// 使用右移 2 位操作获取高位 6 位
	RAM_Profile0[3] = (uint8_t)((LENGTH - 1) >> 2);

	// 将 (LENGTH - 1) 的低位 2 位存储到 RAM_Profile0[4]
	// 使用左移 6 位操作获取低位 2 位
	RAM_Profile0[4] = (uint8_t)((LENGTH - 1) << 6);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 将CFR1写入其寄存器0x00
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0X0E); // 写寄存器RAM_Profile0
    for (i = 0; i < 8; i++)
    {
        txd_8bit(RAM_Profile0[i]); // 将RAM的起始和终止地址、地址步进率写入相应的寄存器  0x0e
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x16);
    uint16_t Zero_Num = LENGTH * Duty / 100;
    for (i = 0; i < Zero_Num; i++)
    {
        PWM_Wave[i] = 0;
    }
    for (i = Zero_Num; i < LENGTH; i++)
    {
        PWM_Wave[i] = voltage;
    }
    for (i = 0; i < LENGTH; i++)
    {
        Write_32bit(PWM_Wave[i] << 18); // 将三角波(或其他任意波形)的数据数组写入ram,32位寄存器,14位DAC值,数据左对齐,故左移18位
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00);  // 将CFR1写入其地址0x00
    CFR1[0] |= 0X80; // 使能RAM
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);

    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
}
/************************************************************
** 函数名称 :Sin_Generate(uint16_t step_rates, uint16_t voltage)
** 函数功能 :根据地址步进率,电压,来产生合适的正弦波
更改地址步进率是更改RAM_Profile0[1]和RAM_Profile0[2]
**************************************************************/
void Sin_Generate(uint16_t step_rates, uint16_t voltage)
{
    int i;
    const uint16_t LENGTH = 100;
    uint32_t Sin_Wave[LENGTH];
    uint8_t CFR1[] = {0x40, 0x40, 0x00, 0x00};                                 // RAM回放目的:幅度;;开启AD9910反Sinc滤波
    uint8_t RAM_Profile0[] = {0x00, 0x00, 0x7A, 0xff, 0xc0, 0x00, 0x00, 0x04}; //	地址步进率0XFFFF=65535,,从地址0回放到1023,共1024点,输出频率 = 1G / (4*122) /1024 =2000Hz  // 连续循环模式RAM_PROx[7]=0x04
	// 将 step_rates 的高位字节存储到 RAM_Profile0[1]
	// 使用右移 8 位操作获取高位字节,然后使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[1] = (uint8_t)(step_rates >> 8) & 0xFF;

	// 将 step_rates 的低位字节存储到 RAM_Profile0[2]
	// 使用按位与操作符与 0xFF 进行掩码操作以确保只取低 8 位
	RAM_Profile0[2] = (uint8_t)step_rates & 0xFF;

	// 将 (LENGTH - 1) 的高位 6 位存储到 RAM_Profile0[3]
	// 使用右移 2 位操作获取高位 6 位
	RAM_Profile0[3] = (uint8_t)((LENGTH - 1) >> 2);

	// 将 (LENGTH - 1) 的低位 2 位存储到 RAM_Profile0[4]
	// 使用左移 6 位操作获取低位 2 位
	RAM_Profile0[4] = (uint8_t)((LENGTH - 1) << 6);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00); // 将CFR1写入其寄存器0x00
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0X0E); // 写寄存器RAM_Profile0
    for (i = 0; i < 8; i++)
    {
        txd_8bit(RAM_Profile0[i]); // 将RAM的起始和终止地址、地址步进率写入相应的寄存器  0x0e
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x16);
    for (i = 0; i < LENGTH; i++)
    {
        Sin_Wave[i] = (float)voltage/2 * sin(2 * PI / 100 * i)+(float)voltage/2;
    }
    for (i = 0; i < LENGTH; i++)
    {
        Write_32bit(Sin_Wave[i] << 18); // 将三角波(或其他任意波形)的数据数组写入ram,32位寄存器,14位DAC值,数据左对齐,故左移18位
    }
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);
    // CS = 0;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_RESET);
    txd_8bit(0x00);  // 将CFR1写入其地址0x00
    CFR1[0] |= 0X80; // 使能RAM
    for (i = 0; i < 4; i++)
        txd_8bit(CFR1[i]);
    // CS = 1;
    HAL_GPIO_WritePin(CSB_GPIO_Port, CSB_Pin, GPIO_PIN_SET);

    // UP_DAT = 0; // 更新AD9910
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
    // UP_DAT = 1;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_SET);
    // UP_DAT = 0;
    HAL_GPIO_WritePin(IOUP_GPIO_Port, IOUP_Pin, GPIO_PIN_RESET);
}

AD9910.h

#ifndef __AD9910_H__
#define __AD9910_H__

#include "main.h"
#include "gpio.h"
#include "math.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long int

typedef enum
{
    TRIG_WAVE = 0,
    SQUARE_WAVE,
    SINC_WAVE,
} AD9910_WAVE_ENUM;

// void AD9110_IOInit(void);      //资料里的引脚初始化函数,没用
void Init_AD9910(void);             // 资料里的引脚初始化函数,没用
void AD9910_FreWrite(ulong Freq);   // 单频调制模式写频率
void AD9910_AmpWrite(uint16_t Amp); // 单频调制模式写电压
void txd_8bit(uchar txdat);
void Write_32bit(uint32_t dat);
// 自己创建的函数在下面
void Init_AD9910_Fre(uint32_t Frequency);                               // 按照频率对AD9910进行初始化
void Reset_AD9910();                                                    // 复位AD9910
void PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty); // 操作AD9910产生PWM
void Sin_Generate(uint16_t PFREQUENCY, uint16_t voltage);               // 操作AD9910产生正弦波
#endif

SI5351A.c

#include "si5351a.h"

// static void I2C_GPIO_Config(void)
//{
//   GPIO_InitTypeDef GPIO_InitStructure;
//   RCC_APB2PeriphClockCmd  (RCC_APB2Periph_GPIOC, ENABLE );
//   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_3;  /* PC3-I2C_SCL、PC5-I2C_SDA*/
//   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
//   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
//   GPIO_Init(GPIOC, &GPIO_InitStructure);
// }

///IIC初始化//
// void IIC_SI5351A_GPIO_Init(void)
// {
//   I2C_GPIO_Config();
// }
粗略延时函数//
void Delay_1us(uint16_t n) // 约1us,1100k
{
    unsigned int x = 5, i = 0;
    for (i = 0; i < n; i++)
    {
        while (x--)
            ;
        x = 5;
    }
}
void Delay_ms(uint8_t n) // 约0.5ms,2k
{
    unsigned int x = 5000, i = 0;
    for (i = 0; i < n; i++)
    {
        while (x--)
            ;
        x = 5000;
    }
}
IIC启动函数//
void I2C_Start(void)
{
    SDA_H;
    SCL_H;
    Delay_1us(1);
    if (!SDA_read)
        return; // SDA线为低电平则总线忙,退出
    SDA_L;
    Delay_1us(1);
    if (SDA_read)
        return; // SDA线为高电平则总线出错,退出
    SDA_L;
    Delay_1us(1);
    SCL_L;
}
//**************************************
// IIC停止信号
//**************************************
void I2C_Stop(void)
{
    SDA_L;
    SCL_L;
    Delay_1us(1);
    SCL_H;
    SDA_H;
    Delay_1us(1); // 延时
}
//**************************************
// IIC发送应答信号
// 入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(uint8_t i)
{
    if (1 == i)
        SDA_H; // 写应答信号
    else
        SDA_L;
    SCL_H;        // 拉高时钟线
    Delay_1us(1); // 延时
    SCL_L;        // 拉低时钟线
    Delay_1us(1);
}
//**************************************
// IIC等待应答
// 返回值:ack (1:ACK 0:NAK)
//**************************************
bool I2C_WaitAck(void) // 返回为:=1有ACK,=0无ACK
{
    unsigned int i;
    SDA_H;
    Delay_1us(1);
    SCL_H;
    Delay_1us(1);
    while (SDA_read)
    {
        i++;
        if (i == 5000)
            break;
    }
    if (SDA_read)
    {
        SCL_L;
        Delay_1us(1);
        return FALSE;
    }
    SCL_L;
    Delay_1us(1);
    return TRUE;
}

//**************************************
// 向IIC总线发送一个字节数据
//**************************************
void I2C_SendByte(uint8_t dat)
{
    unsigned int i;
    SCL_L;
    for (i = 0; i < 8; i++) // 8位计数器
    {
        if (dat & 0x80)
        {
            SDA_H;
        } // 送数据口
        else
            SDA_L;
        SCL_H;        // 拉高时钟线
        Delay_1us(1); // 延时
        SCL_L;        // 拉低时钟线
        Delay_1us(1); // 延时
        dat <<= 1;    // 移出数据的最高位
    }
}

//**************************************
// 从IIC总线接收一个字节数据
//**************************************
uint8_t I2C_RecvByte()
{
    uint8_t i;
    uint8_t dat = 0;
    SDA_H;                  // 使能内部上拉,准备读取数据,
    for (i = 0; i < 8; i++) // 8位计数器
    {
        dat <<= 1;
        SCL_H;        // 拉高时钟线
        Delay_1us(1); // 延时
        if (SDA_read) // 读数据
        {
            dat |= 0x01;
        }
        SCL_L; // 拉低时钟线
        Delay_1us(1);
    }
    return dat;
}
//**************************************
// 向IIC设备写入一个字节数据
//**************************************
bool Single_WriteI2C(uint8_t Slave_Address, uint8_t REG_Address, uint8_t REG_data)
{
    I2C_Start();                 // 起始信号
    I2C_SendByte(Slave_Address); // 发送设备地址+写信号
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_SendByte(REG_Address); // 内部寄存器地址,
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_SendByte(REG_data); // 内部寄存器数据,
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_Stop(); // 发送停止信号
}

uint8_t Single_ReadI2C(uint8_t Slave_Address, uint8_t REG_Address)
{
    uint8_t REG_data;
    I2C_Start();                 // 起始信号
    I2C_SendByte(Slave_Address); // 发送设备地址+写信号
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_SendByte(REG_Address); // 发送存储单元地址,从0开始
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_Start();                     // 起始信号
    I2C_SendByte(Slave_Address + 1); // 发送设备地址+读信号
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    REG_data = I2C_RecvByte(); // 读出寄存器数据
    I2C_SendACK(1);            // 发送停止传输信号
    I2C_Stop();                // 停止信号
    return REG_data;
}

uint8_t i2cSendRegister(uint8_t reg, uint8_t data)
{
    I2C_Start();        // 起始信号
    I2C_SendByte(0xC0); // 发送设备地址+写信号
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_SendByte(reg); // 内部寄存器地址,
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_SendByte(data); // 内部寄存器数据,
    if (!I2C_WaitAck())
    {
        I2C_Stop();
        return FALSE;
    }
    I2C_Stop();
    return 0;
}

void si5351aSetFrequency(uint32_t frequency)
{
    // 局部变量定义
    uint32_t pllFreq;              // PLL频率
    uint32_t xtalFreq = XTAL_FREQ; // 晶体频率,由XTAL_FREQ宏定义指定
    uint32_t l;
    float f;
    uint8_t mult;     // PLL频率乘数
    uint32_t num;     // 分数乘数的分子
    uint32_t denom;   // 分数乘数的分母
    uint32_t divider; // 分频比

    // 计算分频比。900,000,000是内部PLL的最大频率:900MHz
    divider = 900000000 / frequency;
    // 如果divider是奇数,减一使其成为偶数,确保是整数分频比
    if (divider % 2)
        divider--;

    // 计算PLL频率:分频比 * 目标输出频率
    pllFreq = divider * frequency;

    // 确定乘数以达到所需的PLL频率
    mult = pllFreq / xtalFreq;
    // 计算乘数的整数部分和小数部分
    l = pllFreq % xtalFreq;
    f = l;
    // 小数部分的处理:乘以1048575,然后除以晶体频率
    f *= 1048575;
    f /= xtalFreq;
    num = f;
    denom = 1048575; // 简化处理,将分母设为最大值1048575

    // 使用计算出的乘数、分子、分母设置PLL A
    setupPLL(SI_SYNTH_PLL_A, mult, num, denom);

    // 使用计算出的分频比设置MultiSynth除数0。最终R分频阶段可以将频率除以1到128之间的2的幂次
    // 由SI_R_DIV1到SI_R_DIV128常量表示(见si5351a.h头文件)
    // 如果你想输出低于1MHz的频率,必须使用最终的R分频阶段
    setupMultisynth(SI_SYNTH_MS_0, divider, SI_R_DIV_1);

    // 重置PLL。这会导致输出短暂的不稳定。对参数的小改动不需要重置PLL,也就不会有不稳定现象
    i2cSendRegister(SI_PLL_RESET, 0xA0);

    // 最后,打开CLK0输出(0x4F)并设置MultiSynth0的输入为PLL A
    i2cSendRegister(SI_CLK0_CONTROL, 0x4F | SI_CLK_SRC_PLL_A);
}

void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom)
{
    uint32_t P1; // PLL配置寄存器P1
    uint32_t P2; // PLL配置寄存器P2
    uint32_t P3; // PLL配置寄存器P3

    // 计算P1值,首先将分数乘数转换为浮点数进行运算,然后将结果乘以128
    P1 = (uint32_t)(128 * ((float)num / (float)denom));
    // 然后加上乘数mult乘以128,最后减去512
    P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
    // 计算P2值,过程同P1,但是计算方式略有不同
    P2 = (uint32_t)(128 * ((float)num / (float)denom));
    // 使用num乘以128减去denom乘以前面计算的P2值
    P2 = (uint32_t)(128 * num - denom * P2);
    // P3简单地等于分母denom
    P3 = denom;

    // 使用I2C发送寄存器值来配置PLL
    // 发送P3的第二个字节
    i2cSendRegister(pll + 0, (P3 & 0x0000FF00) >> 8);
    // 发送P3的第一个字节
    i2cSendRegister(pll + 1, (P3 & 0x000000FF));
    // 发送P1的最高两位
    i2cSendRegister(pll + 2, (P1 & 0x00030000) >> 16);
    // 发送P1的中间八位
    i2cSendRegister(pll + 3, (P1 & 0x0000FF00) >> 8);
    // 发送P1的最低八位
    i2cSendRegister(pll + 4, (P1 & 0x000000FF));
    // 发送P3和P2的最高四位
    i2cSendRegister(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
    // 发送P2的中间八位
    i2cSendRegister(pll + 6, (P2 & 0x0000FF00) >> 8);
    // 发送P2的最低八位
    i2cSendRegister(pll + 7, (P2 & 0x000000FF));
}

void setupMultisynth(uint8_t synth, uint32_t divider, uint8_t rDiv)
{
    uint32_t P1; // 多合成器配置寄存器P1
    uint32_t P2; // 多合成器配置寄存器P2
    uint32_t P3; // 多合成器配置寄存器P3

    // 计算P1的值,这是基于分频比的配置,128是固定小数点运算的一部分,512用于调整
    P1 = 128 * divider - 512;
    P2 = 0; // 将P2设为0和P3设为1,强制使分频器的值为整数
    P3 = 1;

    // 通过I2C发送命令设置多合成器的配置寄存器
    // 发送P3的高8位
    i2cSendRegister(synth + 0, (P3 & 0x0000FF00) >> 8);
    // 发送P3的低8位
    i2cSendRegister(synth + 1, (P3 & 0x000000FF));
    // 发送P1的最高两位和rDiv。rDiv用于进一步分频,控制输出频率的范围
    i2cSendRegister(synth + 2, ((P1 & 0x00030000) >> 16) | rDiv);
    // 发送P1的中间8位
    i2cSendRegister(synth + 3, (P1 & 0x0000FF00) >> 8);
    // 发送P1的低8位
    i2cSendRegister(synth + 4, (P1 & 0x000000FF));
    // 结合P3和P2的部分高位,虽然这里P2始终为0
    i2cSendRegister(synth + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
    // 发送P2的中间8位,这里始终为0
    i2cSendRegister(synth + 6, (P2 & 0x0000FF00) >> 8);
    // 发送P2的低8位,这里始终为0
    i2cSendRegister(synth + 7, (P2 & 0x000000FF));
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

SI5351A.h

#ifndef __AD9910_H__
#define __AD9910_H__

#include "main.h"
#include "gpio.h"
#include "math.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long int

typedef enum
{
    TRIG_WAVE = 0,
    SQUARE_WAVE,
    SINC_WAVE,
} AD9910_WAVE_ENUM;

// void AD9110_IOInit(void);      //资料里的引脚初始化函数,没用
void Init_AD9910(void);             // 资料里的引脚初始化函数,没用
void AD9910_FreWrite(ulong Freq);   // 单频调制模式写频率
void AD9910_AmpWrite(uint16_t Amp); // 单频调制模式写电压
void txd_8bit(uchar txdat);
void Write_32bit(uint32_t dat);
// 自己创建的函数在下面
void Init_AD9910_Fre(uint32_t Frequency);                               // 按照频率对AD9910进行初始化
void Reset_AD9910();                                                    // 复位AD9910
void PWM_Generate(uint16_t step_rates, uint16_t voltage, uint8_t Duty); // 操作AD9910产生PWM
void Sin_Generate(uint16_t PFREQUENCY, uint16_t voltage);               // 操作AD9910产生正弦波
#endif

最后通过function.c统合文件

function.c

由于这两个数组太大了,所以下面的代码里面没有,但是用下面的生成这个数组的python代码生成即可。

#include "function.h"
#define max_length 1                                        // 宏定义UART接收最大长度
#define Fre_MAX 20000                                       // 最大频率
#define Fre_MIN 20                                          // 最小频率
#define PWM_Duty_MAX 98                                     // 最大占空比
#define PWM_Duty_MIN 2                                      // 最小占空比
#define Voltage_MAX 16383                                   // 最大电压
#define Voltage_MIN 0                                       // 最小电压
uint8_t uart1_rxceive[max_length] = {0};                    // 串口接收数组
int32_t Frequency = 17452;                                  // 频率
int8_t Duty = 50;                                           // 占空比
int16_t Voltage = 16383;                                    // 电压
uint16_t stepsize_auto = 5;                                 // 自动步进的频率
uint8_t Flag_rx = 0;                                        // 标志——串口接收
uint8_t FLag_PWM = 0;                                       // 标志——PWM开启关闭
uint8_t Flag_Sin = 0;                                       // 标志——正弦波开启关闭
uint8_t Flag_Auto = 0;                                      // 标志——频率自动步进开启关闭
uint16_t stepsiez_Duty[4] = {2, 5, 10, 20};                 // 步长——占空比
uint16_t stepsize_Frequency[5] = {1, 5, 100, 1000, 10000};  // 步长——频率
uint16_t stepsize_Voltage[5] = {1, 10, 110, 100, 1000};     // 步长——电压
int16_t index_Frequency = 1;                                // 索引——频率步长
int16_t index_Duty = 0;                                     // 索引——占空比步长
int16_t index_Voltage = 0;                                  // 索引——电压步长
uint8_t Num_Fre = 5 - 1;                                    // 个数——频率步长
uint8_t Num_Duty = 4 - 1;                                   // 个数——占空比步长
uint8_t Num_Voltage = 5 - 1;                                // 个数——电压步长
int32_t Error = 1470;                                       // 频率误差
uint16_t stepisze_Error[] = {1, 3, 5, 10, 100, 1000, 5000}; // 步长——频率误差
int8_t index_Error = 0;                                     // 索引——频率误差
void UART_Start()
{
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart1_rxceive, max_length); // 开启一次中断接收
}

void UART_Process()
{
    if (Flag_rx == 1)
    {
        Flag_rx = 0;                  // 标志位置零
        if (uart1_rxceive[0] == 0x0A) // 开启关闭正弦波的输出
        {
            if (Flag_Sin == 0) // 如果正弦波没有输出
            {
                if (FLag_PWM == 1) // 如果PWM正在输出,就关闭PWM波
                {
                    PWM_Stop();
                }
                Sin_Start(Frequency, Voltage); // 开启正弦波,并打印相关参数
            }
            else if (Flag_Sin == 1)
            {
                Sin_Stop();
            }
        }
        else if (uart1_rxceive[0] == 0x0B) // 开启和关闭PWM的输出
        {
            if (FLag_PWM == 0) // 当PWM关闭时
            {
                if (Flag_Sin == 1)
                {
                    Sin_Stop();
                }
                PWM_Start(Frequency, Duty, Voltage);
            }
            else if (FLag_PWM == 1) // 当PWM打开时
            {
                PWM_Stop();
            }
        }
        else if (uart1_rxceive[0] == 0x0C) // 增大频率
        {
            Fre_Up();
        }
        else if (uart1_rxceive[0] == 0x0D) // 缩小频率
        {
            Fre_Down();
        }
        else if (uart1_rxceive[0] == 0x0E) // 增大频率步进
        {
            Fre_Stepsize_Up();
        }
        else if (uart1_rxceive[0] == 0x0F)// 缩小频率步进
        {
            Fre_Stepsize_Down();
        }
        else if (uart1_rxceive[0] == 0x11) // 增大电压
        {
            Voltage_Up();
        }
        else if (uart1_rxceive[0] == 0x22) // 减小电压
        {
            Voltage_Down();
        }
        else if (uart1_rxceive[0] == 0x33) // 增大电压步进
        {
            Voltage_Stepsize_Up();
        }
        else if (uart1_rxceive[0] == 0x44) // 减小电压步进
        {
            Voltage_Stepsize_Down();
        }
        else if (uart1_rxceive[0] == 0x55) // 增大PWM的占空比
        {
            PWM_Duty_Up();
        }
        else if (uart1_rxceive[0] == 0x66) // 缩小PWM的占空比
        {
            PWM_Duty_Down();
        }

        else if (uart1_rxceive[0] == 0x77) // 增大占空比步进
        {
            Duty_Stepsize_Up();
        }
        else if (uart1_rxceive[0] == 0x88) // 缩小占空比步进
        {
            Duty_Stepsize_Down();
        }

        else if (uart1_rxceive[0] == 0x99)//频率误差增大
        {
            Error_Up();
        }
        else if(uart1_rxceive[0] == 0x00)//频率误差减小
        {
            Error_Down();
        }
        else if(uart1_rxceive[0] == 0x1A)//频率误差步进增大
        {
            Error_Stepsize_Up();
        }
        else if(uart1_rxceive[0] == 0x1B)//频率误差步进增大
        {
            Error_Stepsize_Down();
        }
        else if(uart1_rxceive[0] == 0x1C)//频率自动步进
        {
            if (Flag_Auto == 0)
            {
                Flag_Auto = 1;
            }
            else if (Flag_Auto == 1)
            {
                Flag_Auto = 0;
            }
        }
        else if(uart1_rxceive[0] == 0x1D)//元神启动
        {
           printf("p0.pic=2\xff\xff\xff");
        }
        UART_Start();
    }
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) // 中断接收回调函数
{
    if (huart->Instance == USART1) // 判断串口
    {
        Flag_rx = 1; // 标志位置1
    }
}

void PWM_Start(uint16_t Frequency, uint16_t Duty, uint16_t Voltage)
{
    FLag_PWM = 1;                                                                   // PWM输出标志置为1
    si5351aSetFrequency(step_rates_PWM[Frequency - 20] * 400 * Frequency / 25 - Error); // 信号源设置为合适的信号输出
    Init_AD9910_Fre(step_rates_PWM[Frequency - 20] * 400 * Frequency);                  // AD9910依据信号进行初始化,更改VOC的值
    PWM_Generate(step_rates_PWM[Frequency - 20], Voltage, Duty);                        // 依据频率,占空比来产生PWM波
    printf("t1.txt=\"脉冲波\"\xff\xff\xff");
    printf("t3.txt=\"%dHz\"\xff\xff\xff", Frequency);
    printf("t5.txt=\"%dHz\"\xff\xff\xff", stepsize_Frequency[index_Frequency]);
    printf("t7.txt=\"%dHz\"\xff\xff\xff", Voltage);
    printf("t9.txt=\"%dHz\"\xff\xff\xff", stepsize_Voltage[index_Voltage]);
    printf("t11.txt=\"%d%%\"\xff\xff\xff", Duty);
    printf("t13.txt=\"%d%%\"\xff\xff\xff", stepsiez_Duty[index_Duty]);
    printf("t15.txt=\"%d\"\xff\xff\xff", Error);
    printf("t17.txt=\"%d\"\xff\xff\xff", stepisze_Error[index_Error]);
    printf("p0.pic=1\xff\xff\xff");
}

void Sin_Start(uint16_t Frequency, uint16_t Voltage)
{
    Flag_Sin = 1;
    si5351aSetFrequency(step_rates_Sin[Frequency - 20] * 1600 * Frequency / 25 - Error); // 信号源设置为合适的信号输出
    Init_AD9910_Fre(step_rates_Sin[Frequency - 20] * 1600 * Frequency);                  // AD9910依据信号进行初始化,更改VOC的值
    Sin_Generate(step_rates_Sin[Frequency - 20], Voltage);                              // 依据频率,占空比来产生正弦波
    printf("t1.txt=\"正弦波\"\xff\xff\xff");
    printf("t3.txt=\"%dHz\"\xff\xff\xff", Frequency);
    printf("t5.txt=\"%dHz\"\xff\xff\xff", stepsize_Frequency[index_Frequency]);
    printf("t7.txt=\"%dHz\"\xff\xff\xff", Voltage);
    printf("t9.txt=\"%dHz\"\xff\xff\xff", stepsize_Voltage[index_Voltage]);
    printf("t11.txt=\"%d%%\"\xff\xff\xff", Duty);
    printf("t13.txt=\"%d%%\"\xff\xff\xff", stepsiez_Duty[index_Duty]);
    printf("t15.txt=\"%d\"\xff\xff\xff", Error);
    printf("t17.txt=\"%d\"\xff\xff\xff", stepisze_Error[index_Error]);
    printf("p0.pic=0\xff\xff\xff");
}

void PWM_Stop()
{
    Reset_AD9910();
    FLag_PWM = 0;
	printf("t1.txt=\"     \"\xff\xff\xff");
	printf("t3.txt=\"     \"\xff\xff\xff");
	printf("t5.txt=\"     \"\xff\xff\xff");
	printf("t7.txt=\"     \"\xff\xff\xff");
	printf("t9.txt=\"     \"\xff\xff\xff");
	printf("t11.txt=\"     \"\xff\xff\xff");
	printf("t13.txt=\"     \"\xff\xff\xff");
	printf("t15.txt=\"     \"\xff\xff\xff");
	printf("t17.txt=\"     \"\xff\xff\xff");
    printf("p0.pic=2\xff\xff\xff");
}

void Sin_Stop()
{
    Reset_AD9910();
    Flag_Sin = 0;
	printf("t1.txt=\"     \"\xff\xff\xff");
	printf("t3.txt=\"     \"\xff\xff\xff");
	printf("t5.txt=\"     \"\xff\xff\xff");
	printf("t7.txt=\"     \"\xff\xff\xff");
	printf("t9.txt=\"     \"\xff\xff\xff");
	printf("t11.txt=\"     \"\xff\xff\xff");
	printf("t13.txt=\"     \"\xff\xff\xff");
	printf("t15.txt=\"     \"\xff\xff\xff");
	printf("t17.txt=\"     \"\xff\xff\xff");
    printf("p0.pic=2\xff\xff\xff");
}
void Fre_Up()
{
    Frequency += stepsize_Frequency[index_Frequency];
    if (Frequency >= Fre_MAX)
    {
        Frequency = Fre_MAX;
    }
    Reset_AD9910();
    if (FLag_PWM == 1)
    {
        PWM_Start(Frequency, Duty, Voltage);
    }
    else if (Flag_Sin == 1)
    {
        Sin_Start(Frequency, Voltage);
    }
}
void Fre_Down()
{
    Frequency -= stepsize_Frequency[index_Frequency];
    if (Frequency <= Fre_MIN)
    {
        Frequency = Fre_MIN;
    }
    Reset_AD9910();
    if (FLag_PWM == 1)
    {
        PWM_Start(Frequency, Duty, Voltage);
    }
    else if (Flag_Sin == 1)
    {
        Sin_Start(Frequency, Voltage);
    }
}

void PWM_Duty_Up() // 增大PWM占空比
{
    Duty += stepsiez_Duty[index_Duty]; // PWM的占空比增大指定步长
    if (Duty >= PWM_Duty_MAX)
    {
        Duty = PWM_Duty_MAX; // 占空比最大98%
    }
    PWM_Start(Frequency, Duty, Voltage); // 修改PWM频率,占空比
}

void PWM_Duty_Down() // 减小PWM占空比
{
    Duty -= stepsiez_Duty[index_Duty]; // PWM的占空比缩小指定步长
    if (Duty <= PWM_Duty_MIN)
    {
        Duty = PWM_Duty_MIN; // 占空比最小2%
    }
    PWM_Start(Frequency, Duty, Voltage); // 修改PWM频率,占空比
}

void Fre_Stepsize_Up() // 频率步进增大
{
    index_Frequency += 1;
    if (index_Frequency >= Num_Fre)
    {
        index_Frequency = Num_Fre;
    }
    printf("t5.txt=\"%dHz\"\xff\xff\xff", stepsize_Frequency[index_Frequency]);

}
void Fre_Stepsize_Down() // 频率步进减小
{
    index_Frequency -= 1;
    if (index_Frequency <= 0)
    {
        index_Frequency = 0;
    }
    printf("t5.txt=\"%dHz\"\xff\xff\xff", stepsize_Frequency[index_Frequency]);

}
void Duty_Stepsize_Up() // 占空比步进增大
{
    index_Duty += 1;
    if (index_Duty >= Num_Duty)
    {
        index_Duty = Num_Duty;
    }
    printf("t13.txt=\"%d%%\"\xff\xff\xff", stepsiez_Duty[index_Duty]);
}
void Duty_Stepsize_Down() // 占空比步进减小
{
    index_Duty -= 1;
    if (index_Duty <= 0)
    {
        index_Duty = 0;
    }
    printf("t13.txt=\"%d%%\"\xff\xff\xff", stepsiez_Duty[index_Duty]);
}
void Voltage_Up()
{
    Voltage += stepsize_Voltage[index_Voltage];
    if (Voltage >= Voltage_MAX)
    {
        Voltage = Voltage_MAX;
    }
    if (FLag_PWM == 1)
    {
        PWM_Start(Frequency, Duty, Voltage);
    }
    else if (Flag_Sin == 1)
    {
        Sin_Start(Frequency, Voltage);
    }
}

void Voltage_Down()
{
    Voltage -= stepsize_Voltage[index_Voltage];
    if (Voltage <= Voltage_MIN)
    {
        Voltage = Voltage_MIN;
    }
    if (FLag_PWM == 1)
    {
        PWM_Start(Frequency, Duty, Voltage);
    }
    else if (Flag_Sin == 1)
    {
        Sin_Start(Frequency, Voltage);
    }
}

void Voltage_Stepsize_Up()
{
    index_Voltage += 1;
    if (index_Voltage >= Num_Voltage)
    {
        index_Voltage = Num_Voltage;
    }
    printf("t9.txt=\"%dHz\"\xff\xff\xff", stepsize_Voltage[index_Voltage]);
}
void Voltage_Stepsize_Down()
{
    index_Voltage -= 1;
    if (index_Voltage <= 0)
    {
        index_Voltage = 0;
    }
    printf("t9.txt=\"%dHz\"\xff\xff\xff", stepsize_Voltage[index_Voltage]);
}

void Auto_Stepsize()
{
    if (Flag_Auto == 1)
    {
        Frequency += stepsize_Frequency[index_Frequency];
        if (Frequency >= Fre_MAX)
        {
            Frequency = Fre_MAX;
        }
        if (FLag_PWM == 1)
        {
            PWM_Start(Frequency, Duty, Voltage);
        }
        else if (Flag_Sin == 1)
        {
            Sin_Start(Frequency, Voltage);
        }
        HAL_Delay(200);
    }
}

void Error_Up()
{
    Error += stepisze_Error[index_Error];
    Reset_AD9910();
    PWM_Start(Frequency, Duty, Voltage);
}
void Error_Down()
{
    Error -= stepisze_Error[index_Error];
    Reset_AD9910();
    PWM_Start(Frequency, Duty, Voltage);
}
void Error_Stepsize_Up()
{
    index_Error++;
	if (index_Error >= 6)
	{
		index_Error = 6;
	}
    printf("t17.txt=\"%d\"\xff\xff\xff", stepisze_Error[index_Error]);
}
void Error_Stepsize_Down()
{
    index_Error--;
	if (index_Error >= 6)
	{
		index_Error = 6;
	}
	 printf("t17.txt=\"%d\"\xff\xff\xff", stepisze_Error[index_Error]);
}

function.h

#ifndef _Function_H
#define _Function_H
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "AD9910.h"
#include "si5351a.h"
void UART_Start();                                                         // 打开串口中断接收
void UART_Process();                                                       // 对串口接收到的信息进行处理,最主要的处理函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size); // 串口中断接收回调函数
void PWM_Start(uint16_t Frequency, uint16_t Duty, uint16_t Voltage);       // PWM开始输出
void Sin_Start(uint16_t Frequency, uint16_t Voltage);                      // 正弦波开始输出
void PWM_Stop();                                                           // 停止PWM输出
void Sin_Stop();                                                           // 停止正弦波输出
void Fre_Up();                                                             // 频率增大
void Fre_Down();                                                           // 频率减小
void PWM_Duty_Up();                                                        // 占空比增大
void PWM_Duty_Down();                                                      // 占空比减小
void Fre_Stepsize_Up();                                                    // 频率步进增大
void Fre_Stepsize_Down();                                                  // 频率步进减小
void Duty_Stepsize_Up();                                                   // 占空比步进增大
void Duty_Stepsize_Down();                                                 // 占空比步进减小
void Voltage_Up();                                                         // 电压增大
void Voltage_Down();                                                       // 电压减小
void Voltage_Stepsize_Up();                                                // 电压步进增大
void Voltage_Stepsize_Down();                                              // 电压步进减小
void Auto_Stepsize();                                                      // 频率自动增大
void Error_Up();                                                           // 误差修正值增大
void Error_Down();                                                         // 误差修正值减小
void Error_Stepsize_Up();                                                  // 误差修正值步进增大
void Error_Stepsize_Down();                                                // 误差修正值步进减小
#endif

生成PWM波的步进率的python代码

def calculate_step_rate(frequency):
    min_clock_frequency = 400000000
    max_clock_frequency = 460000000

    for step_rate in range(1, 65536):
        clock_frequency = frequency * 100 * 4 * step_rate
        if min_clock_frequency <= clock_frequency <= max_clock_frequency:
            return step_rate

    return None

# 自动计算频率从20到20000赫兹
unfit_frequencies = []

with open("step_rates3.txt", "w") as file:
    file.write("unsigned int step_rates[] = {\n")
    count = 0
    for frequency in range(20, 20001):
        step_rate = calculate_step_rate(frequency)
        if step_rate is not None:
            file.write(f"    /* {frequency} Hz */ {step_rate}, ")
            count += 1
            if count % 200 == 0:  # 如果达到200个数据,换行
                file.write("\n")
        else:
            unfit_frequencies.append(frequency)
    file.write("\n};")

# 输出找不到合适步进率的频率
if unfit_frequencies:
    with open("step_rates3.txt", "a") as file:
        file.write("\n\n找不到合适步进率的频率:\n")
        for frequency in unfit_frequencies:
            file.write(f"{frequency}\n")

print("结果已保存到 step_rates3.txt 文件中。")

生成正弦波的步进率的python代码

def calculate_step_rate(frequency):
    min_clock_frequency = 400000000
    max_clock_frequency = 460000000

    for step_rate in range(1, 65536):
        clock_frequency = frequency * 400 * 4 * step_rate
        if min_clock_frequency <= clock_frequency <= max_clock_frequency:
            return step_rate

    return None

# 自动计算频率从20到20000赫兹
unfit_frequencies = []

with open("sin_step_rates.txt", "w") as file:
    file.write("unsigned int step_rates[] = {\n")
    count = 0
    for frequency in range(20, 20001):
        step_rate = calculate_step_rate(frequency)
        if step_rate is not None:
            file.write(f"    /* {frequency} Hz */ {step_rate}, ")
            count += 1
            if count % 200 == 0:  # 如果达到200个数据,换行
                file.write("\n")
        else:
            unfit_frequencies.append(frequency)
    file.write("\n};")

# 输出找不到合适步进率的频率
if unfit_frequencies:
    with open("sin_step_rates.txt", "a") as file:
        file.write("\n\n找不到合适步进率的频率:\n")
        for frequency in unfit_frequencies:
            file.write(f"{frequency}\n")

print("结果已保存到 sin_step_rates.txt 文件中。")

两个python函数的区别就是PWM的波表长度为100,正弦波的波表长度为400,改一下计算公式即可。

python代码的生成原理如下:

main.c在包含头文件后这样写就行了

main.c

  HAL_Delay(100); // 为了使UART和DMA上电稳定后再打开UART的DMA接收,否则单片机上电后串口屏不能正常工作
  UART_Start();
	UART_Process();//串口处理函数
	  
    Auto_Stepsize();//频率自动步进

在此过程中出现的问题以及解决方法

1.单片机的堆栈区设置小了,CubeMX默认是0x200,当进行一个1024数组的循环生成时会跳到HardFault_Handler这个硬件异常状态。并且不会运行下面的代码。

2.当变量定义为uint时,变量小于0时会变成65535,对于哪些一般都是正数,但是会减一个数有变得小于0的风险的数要设置为int,比如该项目中的频率,占空比等,它们减去频率步进,占空比步进时会有小于0的风险,所以要设置为int。

3.32位定时器产生可调频率PWM,在调节频率时会突然罢工,H723ZGT6和F407ZGT6都是这样,当把定时器的auto reload enable打开时就不会了,但是第一次打开PWM会卡一会。

4.单片机上电后会正常的工作,但是打开串口的DMA接收之前要延时一段时间(100ms),等单片机上电稳定后再打开串口的DMA接收,这样的话单片机上电就能正常工作了。

5.代码中Sin_stop()等函数会把AD9910复位,AD9910复位之后要再次重新初始化一次,不然不能正常工作,所有的模块应该都是这样,模块不能正常工作时记得想一想是不是没有初始化。

6.keil5开版本6可以加快编译速度,但是不会go to definition 并且keil5的版本6有Bug,不会正确编译,不会正确调试程序,有可能下载到单片机里是错误的,所以把版本6当作一个找错误的工具还行,下载和调试还是得用版本5.

7.莫名其妙的错误,SI5351接到G2 G4 G7 C9 这4个引脚不管用,接到D12 F9 F8 F10这四个引脚就管用了,有时候穷途末路的时候可以改一下引脚

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/517634.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图像分割-综述篇

文章目录 图像分割算法类型全卷积FCNSegNetUNetDeeplab v1PSPNetDeeplab v2Deeplab v3Deeplab v3 基于候选区Mask RCNNMS RNN 基于GAN基于RNNReSegViTSwin TransformerSAM(Segment Anything Model) 图像分割算法类型 正如我在目标检测系列中提到的&#xff0c;图像分割&#x…

ubuntu无法粘贴复制windows中的内容,分辨率无法自适应电脑自带系统

1、直接在命令行执行以下命令 sudo apt-get autoremove open-vm-tools //卸载已有的工具 sudo apt-get install open-vm-tools //安装工具open-vm-tools sudo apt-get install open-vm-tools-desktop //安装open-vm-tools-desktop 2、重启Ubuntu系统即可 3.如果上述…

Selenium的简单防反爬和浏览器配置

# Selenium的简单使用&#xff1a;https://zhuanlan.zhihu.com/p/557463669 # 防反爬参考&#xff1a;https://blog.csdn.net/weixin_51368459/article/details/125462178 from selenium import webdriver from selenium.webdriver.edge.options import Options# 设置浏览器驱动…

浙大恩特客户资源管理系统 CompInfoAction SQL注入漏洞复现

0x01 产品简介 浙大恩特客户资源管理系统是一款针对企业客户资源管理的软件产品。该系统旨在帮助企业高效地管理和利用客户资源,提升销售和市场营销的效果。 0x02 漏洞概述 浙大恩特客户资源管理系统 CompInfoAction 接口存在 SQL 注入漏洞,攻击者可通过输入恶意 SQL 代码…

管道的用法

一、fork 的用法 fork 返回值 c 在C中&#xff0c;fork 是一个来自 Unix/Linux 系统的系统调用&#xff0c;用于创建一个与现有进程几乎完全相同的新进程。fork 的主要特点是它会返回两次&#xff0c;一次返回在父进程中&#xff0c;一次返回在子进程中。在父进程中&#xff…

mysql ideal中使用 基本数据类型

新建数据库 新建表 提交到数据库 新建01数据类型.sql 注释 多行 /**/ 单行 #+空格,空格不能省略 数据类型 1.整数型: tinyint:占用一个字节,取值范围是[-128,127] 相当于Java中的 byte 类型 smallnt:占用两个字节、取值范围是[-2的15次方,2的15次方-1] short类型 med…

3.Swagger整合

一、引入相关依赖 <!-- 图像化依赖 --> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version> </dependency> <!--引入swagger2依赖 --> <d…

记Kubernetes(k8s)初始化报错:“Error getting node“ err=“node \“k8s-master\“ not found“

记Kubernetes&#xff08;k8s&#xff09;初始化报错&#xff1a;"Error getting node" err"node \"k8s-master\" not found" 1、报错详情2、问题排查3、尝试问题解决 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#…

网络安全 | 什么是单点登录SSO?

关注WX&#xff1a;CodingTechWork SSO-概念 单点登录 (SSO) 是一种身份认证方法&#xff0c;用户一次可通过一组登录凭证登入会话&#xff0c;在该次会话期间无需再次登录&#xff0c;即可安全访问多个相关的应用和服务。SSO 通常用于管理一些环境中的身份验证&#xff0c;包…

python调用java中的jar

一、基于IDEA生成可执行jar包 1、编写class的代码&#xff0c;注意一定要有main()方法才可以生成jar包&#xff0c;main()方法可以没有内容。例如下Java 代码&#xff1a; package Project;public class Demo {public static void main(String[] args){Demo t2 new Demo();S…

挑战传统编程范式:Java函数式编程的魅力

1.概述 1.1概念 函数式编程是一种编程范式&#xff0c;即一切都是数学函数。在Java面向对象编程中&#xff0c;程序是一系列相互作用&#xff08;方法&#xff09;的对象&#xff0c;而在函数式编程中&#xff0c;程序会是一个无状态的函数组合序列。 1.2 优点 大数量下处理…

【IoTDB 线上小课 01】我们聊聊“金三银四”下的开源

关于 IoTDB&#xff0c;关于物联网&#xff0c;关于时序数据库&#xff0c;关于开源...你是否仍有很多疑问&#xff1f; 除了自己钻研文档&#xff0c;群里与各位“大佬”的沟通&#xff0c;你是否还希望能够有个学习“捷径”&#xff1f; 天谋科技发起社区小伙伴&#xff0c;正…

Hadoop-Yarn

一、Yarn资源调度器 思考&#xff1a; 1&#xff09;如何管理集群资源&#xff1f; 2&#xff09;如何给任务合理分配资源&#xff1f; Yarn 是一个资源调度平台&#xff0c;负责为运算程序提供服务器运算资源&#xff0c;相当于一个分布式的操作系统平台。 而 MapReduce …

EfficientVMamba实战:使用 EfficientVMamba实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

paddlepaddle模型转换onnx指导文档

一、检查本机cuda版本 1、右键找到invdia控制面板 2、找到系统信息 3、点开“组件”选项卡&#xff0c; 可以看到cuda版本&#xff0c;我们这里是cuda11.7 cuda驱动版本为516.94 二、安装paddlepaddle环境 1、获取pip安装命令 &#xff0c;我们到paddlepaddle官网&#xff…

【数据分析面试】6.计算对话总数(SQL)

题目&#xff1a;计算对话总数 给定了名为 messenger_sends 的消息发送表格&#xff0c;找出总共有多少个唯一的对话。 注&#xff1a;在某些记录中&#xff0c;receiver_id 和 sender_id 从初始消息中互换了。这些记录应视为同一个对话。 示例&#xff1a; 输入&#xff1…

flink源码编译-job提交

1、启动standalone集群的taskmanager standalone集群中的taskmanager启动类为 TaskManagerRunner 2 打开master启动类 通过 ctrln快捷键&#xff0c;找到、并打开类&#xff1a; org.apache.flink.runtime.taskexecutor.TaskManagerRunner 3 修改运⾏配置 基本完全按照mas…

『python爬虫』巨量http代理使用 每天白嫖1000ip(保姆级图文)

目录 注册 实名得到API链接和账密 Python3requests调用Scpay总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 注册 实名 注册巨量http 用户概览中领取1000ip,在动态代理中使用.用来测试一下还是不错的 得到AP…

四、MySQL读写分离之MyCAT

一、读写分离概述 1、什么是读写分离&#xff1a; 读写分离&#xff1a;就是将读写操作分发到不同的服务器&#xff0c;读操作分发到对应的服务器 &#xff08;slave&#xff09;&#xff0c;写操作分发到对应的服务器&#xff08;master&#xff09; ① M-S (主从) 架构下&…

前端路径问题总结

1.相对路径 不以/开头 以当前资源的所在路径为出发点去找目标资源 语法: ./表示当前资源的路径 ../表示当前资源的上一层路径 缺点:不同位置,相对路径写法不同2.绝对路径 以固定的路径作为出发点作为目标资源,和当前资源所在路径没关系 语法:以/开头,不同的项目中,固定的路径…