当前位置:   article > 正文

STM笔记_AD9833的详解及其f407驱动_ad9833驱动程序详解

ad9833驱动程序详解

2024/7/28更新

鉴于太多人问我要驱动,但是我这边每次只能给五个陌生人发送消息,所以我把驱动上传到了github上,大家可以点下边的链接直接下载
https://github.com/berlin6699/AD9833
如果觉得有用的话,麻烦给个star,感谢大家!
PS:github上还有一些其他的工程,例如QT写的一些东西和电赛的一些往年题,大家也可以按需自取,希望可以给个star!~
在这里插入图片描述

参考文章

【STM32+cubemx】HAL库开发:DDS芯片AD9833实现简单的波形发生器
这篇文章写的很简短,跳过了很多前置知识,特别是代码部分需要看完下边的笔记才能理解

前言

这是我第一次认真对着一个模块的数据手册完完整整的读完,了解他的通信规则,了解他的寄存器作用,再到最后读懂他的 HAL 库驱动程序。一共花了将近 6 个小时,刚开始看确实很艰难,完全看不下去,不过只要拿起笔,认真边写边画,就会看的比较清楚了

笔记

STM32部分

SPI 接口

写在前边

SPI 我也没有系统的用过,所以这里对 SPI 的讲解,只包含本次驱动所需要使用的部分

SPI 的连线

SPI 共有四个线

  • SCK
  • MOSI
  • MISO
  • SSIx(片选)
SCK

SCK 是 SPI 输出的时钟。对于 STM32F407 来说,共有 2 个 SPI——SPI1、SPI2。他们的 SCK 分别来自于 PCLK 1 和 PCLK 2,自身还有分频系数,可以对输入时钟进行分频
AD9833的HAL库驱动-SPI配置.png

  • SPI 就按如图配置即可
  • 下面介绍几个参数
    • Prescaler
      • 即分频系数,他对输入时钟(PCLKx)进行分频,并将分频后的信号作为 SCK 输出
    • Clock Polarity
      • 即 SCK 初始的状态,High 即代表初始为高
    • GPIO_Output
      • 作为 SPI 的片选信号,用来选中使能AD9833
MOSI
  • Master output slave ouput
    • 即主机(STM32)通过 MOSI 发出数据,让连接并且被使能的从机接收数据的通路
MISO
  • Master input slave output
    • 即主机(STM 32)通过 MISO 接收数据,让连接并且被使能的从机发出数据的通路,但在AD9833里不使用,因为没有需要他发送的数据
SSLx
  • 片选信号,在上图中即表示为 PC1

AD9833部分

输出公式

在DDS技术中,输出频率 f o u t f_{out} fout 与参考时钟频率 f r e f f_{ref} fref 和频率控制字 F C W F_{CW} FCW 之间的关系可以表示为:
f o u t = F C W 2 n f r e f f_{out}=\frac{F_{CW}}{2^n}f_{ref} fout=2nFCWfref

引脚配置和功能描述

AD9833的HAL库驱动-AD9833引脚配置和功能描述.png

我们重点关注高亮的引脚

  • MCLK
    • 数字时钟输入,一般由板载外部晶振提供时钟,通常设置为25MHz,这个也是决定 DDS 输出信号极限频率大小的因素
  • SDATA
    • 串行数据输入,对应连接 SPI 的 [[AD9833的HAL库驱动#^MOSI|MOSI]],进行数据的传输
  • SCLK
    • 串行时钟输入,决定数据输入的速度,对应连接 SPI 的SCK,在时钟的下降沿输入数据
  • FSYNC
    • 片选信号,对应连接 SPI 的SSLx,低有效
  • VOUT
    • 电压输出,DDS 产生的波形就从这个端口输出

寄存器

  • AD9833 共有 3 个寄存器,依次来介绍
寄存器的写入
  • AD9833一次写入 2 字节(16 bit) 的数据,通过特殊的位来区分写入的是哪个寄存器
  • 其中最重要的就是 D15/14/13 位,下边会详细介绍他们的具体操作
控制寄存器

AD9833的HAL库驱动-频率寄存器位.png

在这里插入图片描述

  • D15、D14
    • 当要告知AD9833控制寄存器的内容将更新,D15和D14必须置0。也就是说,必须先有 16 bit 用来告知更新,详情见后边的一些例子
    • 01 代表写入FREQ0、10 代表写入FREQ1
  • D13
    • 控制写入的是连续两次(存满一个频率寄存器),还是单独一次(改变频率寄存器里一半的值)
    • 1:将一个完整字通过两次连续写入载入频率寄存器,第一次写 LSB,第二次写 MSB。由于16位的前两位均是区别写入寄存器的,且连续写入必为写入同一寄存器,故D13为1时,在两次写入操作时,D15/14 不会改变
    • 0:代表只写入某一寄存器的一半,具体是 LSB 还是 MSB 由 D12 决定
  • D12 ^D12
    • 当 D13=1 时,该位没用
    • 当 D13=0 时
      • 该位为 0:LSB
      • 该位为 1:MSB
  • D8
    • 1:将内部寄存器复位为 0
    • 0:禁用复位
    • 复位操作不会使相位、频率或控制寄存器复位。AD9833上电时,器件应复位。为了避免AD9833初始化时产生杂散DAC输出,要使AD9833复位,应将reset bit置1。要使器件退出复位,应将该位清0。在reset置0后的8个MCLK周期内, DAC输出端会出现信号
  • SLEEP
    • 降低功耗的一些操作
  • D5和D1和D3一起使用
    AD9833的HAL库驱动-D5D1.png

解释

  • DAC 数据的 MSB 即 DAC 所储存的数据的最高位,所以只可能是 0 或者 1,因此就是输出方波,不过基本不用
频率寄存器+相位寄存器

Pasted image 20240408222320.png

  • 模拟输出应该指的是输出的频率
  • 偏移指的是偏移的相位

一些例子

1

在这里插入图片描述

解释

  • 第一行:00 代表要数据更新 ,1 代表连续写入两组
  • 第二行:写入 LSB
  • 第三行:写入 MSB
  • 拼接后:MSB-LSB,发现正好是 0xFFFC000
2

AD9833的HAL库驱动-例子2.png

解释

  • 第一行:00 代表要数据更新,00 代表选择LSB
  • 第二行:10 代表写入的寄存器
3

AD9833的HAL库驱动-例子3.png

解释

  • 同上
4 写入相位寄存器
  • 写入相位寄存器时,BitD15和BitD14设置为11。BitD13确定将载入的相位寄存器

STM32的软件驱动

ad9833.c

#include "ad9833.h"

#include "spi.h"

  

void AD9833_GPIOinit(void)

{

CS_9833_1(); //首先拉高片选,不使能,防止输入错误数据

}



/*

*******************************************************

向AD9833发送一个16bit的数据

*******************************************************

*/

void AD9833_Write(unsigned short TxData) //TxData是2字节

{

unsigned char data[2] ; //一个char一个字节,数组为2个字节

data[0] = (unsigned char)((TxData>>8) &0xff); //data[0]存储高位

data[1] = (unsigned char)(TxData&0xff); //data[1]存储低位

CS_9833_0(); //拉低片选,准备写入

HAL_SPI_Transmit (&hspi2 , data, 2, 0x02) ; //用HAL库的SPI发送函数发送数据

CS_9833_1(); //发送完毕,拉高片选

}

  

/*

*******************************************************

Reset: 0为有输出,1为没输出,此位只控制有无输出,不复位寄存器

SleeppMode: 3为关闭内部DAC和时钟,0为不关闭

optionbit|modebit: 00正弦01三角10方波11保留

*******************************************************

*/

void AD9833_CtrlSet(unsigned char Reset,unsigned char SleeppMode,unsigned char optionbit,unsigned char modebit)

{

unsigned short regtemp = 0; //对输出模式的一些选择

regtemp = regtemp|(((unsigned short)Reset&0x01)<<8); //以下就是把每个位对应到相应的寄存器上,不过好像少了个DIV2,DIV2默认为0了?

regtemp = regtemp|((SleeppMode&0x03)<<6);

regtemp = regtemp|((optionbit&0x01)<<5);

regtemp = regtemp|((modebit&0x01)<<1);

  

AD9833_Write(regtemp); //写入数据,不过不需要先声明将要写入吗?

}

  

/*

*******************************************************

设置频率,设置完后暂不输出,用CtrlSet函数设置输出

频率值:0.1Hz-12.5MHz(最大值为25M晶振时钟的一半)

单位:Hz;例如,输出1M,则输入1000000

*******************************************************

*/

void AD9833_FreqSet(double Freq) //Freq是用户输入的以Hz为单位的频率

{

int frequence_LSB,frequence_MSB; //LSB和MSB分别对应频率寄存器里的LSB和MSB

double frequence_mid,frequence_DATA; //mid是一个中间值,用于将输入频率转换为AD9833可以接受的格式;DATA为输入进去的频率的十进制

long int frequence_hex; //hex为输入进去的频率的16进制

  

frequence_mid = 268435456/25; //f_{out}=dds输出频率;F_{cw}=f_{DATA};f_{ref}=25M;2^28=268435456;1000000=1M

frequence_DATA = Freq; //公式:f_{out}=(F_{cw})*(f_{ref})/(2^28)=(f_{DATA})*25M/(2^28)

frequence_DATA = frequence_DATA/1000000; //这里的f_{DATA}(化作以M为单位)=(Freq/1000000)*[(2^28)/(25)],;f_{DATA}作为f_{cw}输入进公式,刚好得到f_{out}=Freq,且以Hz为单位

frequence_DATA = frequence_DATA*frequence_mid;

frequence_hex = frequence_DATA;

frequence_LSB = frequence_hex;

frequence_LSB = frequence_LSB&0x3fff; //以下就是一些与或操作,用来输入寄存器

frequence_MSB = frequence_hex>>14;

frequence_MSB = frequence_MSB&0x3fff;

  

frequence_LSB = frequence_LSB|0x4000;

frequence_MSB = frequence_MSB|0x4000; //4000==0100 0000 0000 0000,把前两位变成01,代表freq0寄存器

  

AD9833_Write(0x2100); //2100==0010 0001 0000 0000;bit8为1->不reset;0010->连续写LSB、MSB标志

AD9833_Write(frequence_LSB);

AD9833_Write(frequence_MSB);

  

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143

ad9833.h

#include "main.h"

  

#define CS_9833_0() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET)

#define CS_9833_1() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET)

  

void AD9833_GPIOinit(void);

void AD9833_Write(unsigned short TxData);

void AD9833_CtrlSet(unsigned char Reset,unsigned char SleeppMode,unsigned char optionbit,unsigned char modebit);

void AD9833_FreqSet(double Freq);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/959325
推荐阅读
相关标签
  

闽ICP备14008679号