使用STM32F103ZET6的HAL库和cubeMX编写AD9834的驱动代码,AD9834的引脚为PS,FS,FSY,SCK,SDA,RST
时间: 2025-07-21 09:01:25 浏览: 4
<think>我们使用STM32CubeMX配置SPI和GPIO,然后使用HAL库编写驱动代码。
AD9834是一款可编程波形发生器,通过SPI接口进行控制。根据数据手册,AD9834的通信时序要求:
- SCLK:时钟信号
SDATA:数据输入(MOSI)
FSYNC(或FS):片选信号,低电平有效。
此外,还有PS(频率选择引脚,用于选择频率寄存器)、RST(复位引脚)等。
引脚连接假设(根据你的需求,请根据实际连接修改):
- PS: 可以连接到GPIO,用于选择频率寄存器(FREQ0或FREQ1)
- FS/FSYNC: 连接到SPI的NSS(片选)引脚,或者用普通GPIO模拟(这里我们使用软件控制GPIO作为FSYNC)
- SCK: SPI时钟
- SDA: SPI的MOSI(主设备输出)
- RST: 复位引脚,连接到GPIO
注意:AD9834的SPI模式:根据数据手册,在SCLK的下降沿数据被锁存(即上升沿采样)。因此,SPI模式应设置为模式2(CPOL=1, CPHA=1)或模式3(CPOL=1, CPHA=0)?
实际上,AD9834要求在SCLK的下降沿数据有效,所以主机应在上升沿输出数据,下降沿被从机采样。因此,STM32作为主机,应配置为:
- CPOL = 1(空闲时高电平)
- CPHA = 1(在第二个边沿采样,即下降沿)
这对应SPI模式3(在STM32中,HAL库的SPI_MODE3)。
但是,请注意:AD9834的数据手册(图22)时序图显示:FSYNC变低后,在SCLK的下降沿数据被锁存。因此,我们选择SPI模式3(CPOL=1, CPHA=1)即可。
步骤:
1. 使用STM32CubeMX配置SPI和GPIO。
2. 初始化相关引脚。
3. 编写驱动函数,包括初始化AD9834、写入数据、设置波形、频率、相位等。
由于AD9834的FSYNC(片选)要求:在传输期间保持低电平,传输完成后拉高。我们可以使用软件控制一个GPIO作为FSYNC(而不是硬件NSS),这样更灵活。
因此,在CubeMX中:
- 配置一个SPI(例如SPI1),模式为全双工主模式,数据大小16位(因为AD9834每次传输16位),CPOL=High,CPHA=2Edge(即模式3)。
- 注意:由于AD9834的数据是16位的,所以设置SPI的数据大小为16位。
- 另外配置几个GPIO作为输出:
PS: 频率选择(可以选择连接到两个GPIO,分别控制FREQ0和FREQ1?但AD9834只有一个PS引脚,它实际上是一个选择位:PS=0选择FREQ0/PHASE0,PS=1选择FREQ1/PHASE1)
FSYNC: 片选(我们使用软件控制,所以配置为GPIO输出,初始高电平)
RST: 复位引脚(低电平复位,所以初始为高电平)
注意:AD9834的通信是16位数据,每次传输16位(两个字节)。在HAL库中,我们使用HAL_SPI_Transmit函数,并设置数据为16位格式。
驱动编写步骤:
1. 初始化GPIO和SPI。
2. 复位AD9834:拉低RST引脚至少10ns(一般延时1us即可),然后拉高。
3. 配置AD9834:通过SPI发送控制字。
控制字的格式:
- AD9834的16位控制字中,最高两位(DB15、DB14)为地址位,用于选择写入的是控制寄存器、频率寄存器还是相位寄存器。
- 具体请参考AD9834数据手册。
我们编写函数:
- void AD9834_Reset(void); // 复位
- void AD9834_WriteReg(uint16_t data); // 写一个16位数据
- void AD9834_SetFrequency(uint32_t freq, uint8_t freqReg); // 设置频率,freqReg为0或1
- void AD9834_SetPhase(uint16_t phase, uint8_t phaseReg); // 设置相位
- void AD9834_SelectOutput(uint8_t freqReg, uint8_t phaseReg); // 选择输出的频率和相位寄存器
- void AD9834_SetWaveform(uint8_t type); // 设置波形类型:正弦波、三角波、方波
注意:AD9834的输出频率由28位寄存器决定,所以需要分两次写入(每次16位,分高14位和低14位写入)。频率计算公式:
f_out = (freq_reg * f_mclk) / (2^28)
其中freq_reg为28位的值。
步骤:
1. 在CubeMX中配置SPI1(假设使用SPI1)和GPIO。
2. 生成代码。
3. 添加驱动代码。
具体操作:
一、CubeMX配置:
- 选择SPI1,配置为全双工主模式,硬件NSS选择Disable(因为我们使用软件控制FSYNC),波特率根据情况设置(不要超过AD9834支持的最大SCLK频率,一般为25MHz)。
- 参数设置:
Prescaler: 设置分频,使SCLK不超过25MHz(如果系统时钟72MHz,分频系数4,则18MHz)
Clock Polarity: High
Clock Phase: 2 Edge
Data Size: 16 bits
First Bit: MSB
- 配置GPIO:
FSYNC: 输出引脚(如PA4),初始状态为高电平(GPIO_PULLUP)
RST: 输出引脚(如PA5),初始状态为高电平
PS: 输出引脚(如PA6),初始状态为低电平(根据需求)
二、代码编写:
首先,定义引脚:
*/
#define AD9834_FSYNC_PIN GPIO_PIN_4
#define AD9834_FSYNC_PORT GPIOA
#define AD9834_RST_PIN GPIO_PIN_5
#define AD9834_RST_PORT GPIOA
#define AD9834_PS_PIN GPIO_PIN_6
#define AD9834_PS_PORT GPIOA
// 复位函数
void AD9834_Reset(void) {
HAL_GPIO_WritePin(AD9834_RST_PORT, AD9834_RST_PIN, GPIO_PIN_RESET); // 复位
HAL_Delay(1); // 延时1ms(实际只需要10ns以上,1ms足够)
HAL_GPIO_WritePin(AD9834_RST_PORT, AD9834_RST_PIN, GPIO_PIN_SET); // 结束复位
}
// 写寄存器函数
void AD9834_WriteReg(uint16_t data) {
// 拉低FSYNC
HAL_GPIO_WritePin(AD9834_FSYNC_PORT, AD9834_FSYNC_PIN, GPIO_PIN_RESET);
// 发送数据
HAL_SPI_Transmit(&hspi1, (uint8_t*)&data, 1, 100); // 注意:这里第三个参数是发送的数据数量,以数据单元为单位。由于数据是16位,所以一个数据单元是2个字节,但数量写1。
// 等待发送完成
// 拉高FSYNC
HAL_GPIO_WritePin(AD9834_FSYNC_PORT, AD9834_FSYNC_PIN, GPIO_PIN_SET);
}
// 设置频率寄存器(freqReg: 0或1)
void AD9834_SetFrequency(uint32_t freq, uint8_t freqReg) {
// 计算频率字:freq_reg = (freq * 2^28) / f_mclk
// 假设f_mclk = 25MHz(根据你实际使用的MCLK频率)
uint32_t f_mclk = 25000000; // 25MHz
uint32_t freq_word = (freq * 268435456UL) / f_mclk; // 2^28=268435456
// 限制在28位
freq_word &= 0x0FFFFFFF;
// 分两次写入:先写低14位(bit0-13),再写高14位(bit14-27)
uint16_t low14 = (uint16_t)(freq_word & 0x3FFF); // 低14位
uint16_t high14 = (uint16_t)((freq_word >> 14) & 0x3FFF); // 高14位
// 写入低14位,并选择频率寄存器(freqReg)
uint16_t reg = 0;
if (freqReg == 0) {
reg = (0 << 14) | (1 << 15); // 控制字:DB15=0, DB14=1 -> 写入FREQ0寄存器的低14位
} else {
reg = (1 << 14) | (1 << 15); // 写入FREQ1寄存器的低14位
}
AD9834_WriteReg(reg | low14);
// 写入高14位
if (freqReg == 0) {
reg = (0 << 14) | (0 << 15); // DB15=0, DB14=0 -> 写入FREQ0寄存器的高14位
} else {
reg = (1 << 14) | (0 << 15); // 写入FREQ1寄存器的高14位
}
AD9834_WriteReg(reg | high14);
}
// 设置相位寄存器(phaseReg:0或1)
void AD9834_SetPhase(uint16_t phase, uint8_t phaseReg) {
// 相位字是12位,所以phase取低12位
phase &= 0x0FFF;
uint16_t reg = 0;
if (phaseReg == 0) {
reg = (0 << 14) | (0 << 15) | (1 << 13); // DB15=0, DB14=0, DB13=1 -> 写入PHASE0寄存器
} else {
reg = (1 << 14) | (0 << 15) | (1 << 13); // 写入PHASE1寄存器
}
AD9834_WriteReg(reg | phase);
}
// 选择输出(选择哪个频率寄存器和相位寄存器)
void AD9834_SelectOutput(uint8_t freqReg, uint8_t phaseReg) {
// 设置PS引脚(PS=0选择FREQ0/PHASE0,PS=1选择FREQ1/PHASE1)
// 注意:AD9834的PS引脚是用于选择当前使用的频率和相位寄存器。
// 但是,我们也可以通过控制字中的FSELECT位(控制寄存器)来选择,而PS引脚是硬件选择?
// 根据数据手册,AD9834有两种模式:通过PS引脚选择,或者通过控制寄存器中的FSELECT位选择(由控制寄存器的位12控制选择方式)。
// 这里我们假设使用PS引脚选择(即控制寄存器的位12=0),那么我们就控制PS引脚。
// 因此,我们设置PS引脚的状态:freqReg的值(0或1)直接给PS引脚。
HAL_GPIO_WritePin(AD9834_PS_PORT, AD9834_PS_PIN, freqReg ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 同时,我们也可以使用软件方式(控制寄存器)来选择,这里为了简单,我们使用硬件PS引脚。
// 如果使用软件方式,则不需要控制PS引脚,而是通过控制字设置FSELECT位。
// 这里我们按照硬件PS引脚的方式。
// 另外,相位寄存器的选择由控制字中的PSELECT位(控制寄存器位10)决定。但是,如果我们使用硬件PS引脚,那么相位寄存器也是由PS引脚选择的(即和频率寄存器选择同一个序号)。
// 所以,我们只需要设置频率寄存器的选择(即PS引脚)即可,相位寄存器自动和频率寄存器使用同一个序号。
}
// 设置波形
void AD9834_SetWaveform(uint8_t type) {
// type: 0-正弦波,1-三角波,2-方波
uint16_t control = 0x2000; // 复位操作(控制寄存器位8=1,即RESET位)并设置模式
// 首先,复位DAC和内部寄存器(控制寄存器位8=1)
// 然后,根据波形设置模式位(控制寄存器位5和位1)
// 正弦波:MODE=0(默认),OPBITEN=0(默认)
// 三角波:MODE=1(控制寄存器位5=1),OPBITEN=0
// 方波:MODE=0,OPBITEN=1,并且DIV2位(控制寄存器位3)决定是输出方波还是输出DAC的MSB(我们这里设为0,输出方波)
// 控制寄存器:DB15=0, DB14=0, DB13=0(控制寄存器地址)
// 先发送复位,然后设置波形
control = 0x2100; // 复位(RESET=1)并设置SLEEP1=1(让DAC和内部时钟工作)?这里我们根据需求设置。
// 实际设置波形时,我们不需要复位,所以先取消复位(控制寄存器位8=0)
// 重新设置控制寄存器:
control = 0x2000; // 控制寄存器地址(DB15=0, DB14=0, DB13=0),且RESET=0
switch (type) {
case 0: // 正弦波
control |= 0; // MODE=0, OPBITEN=0
break;
case 1: // 三角波
control |= (1 << 5); // MODE=1
break;
case 2: // 方波
control |= (1 << 1); // OPBITEN=1
// 同时,可以选择DIV2(控制寄存器位3)来设置输出方波的频率是主时钟的1/2还是1/1?这里我们设为0(输出频率为freq_reg决定的频率)
control |= (0 << 3); // DIV2=0
break;
}
AD9834_WriteReg(control);
}
// 初始化AD9834
void AD9834_Init(void) {
// 初始化GPIO和SPI已经在CubeMX生成的代码中完成
AD9834_Reset(); // 复位
// 设置控制寄存器:先让DAC和内部时钟工作(取消睡眠模式)
// 控制寄存器:设置SLEEP1=0, SLEEP12=0(控制寄存器位6和位7)
uint16_t control = 0x2000; // 控制寄存器地址(0x2000表示写入控制寄存器,且RESET=0, 其他位暂时为0)
control &= ~(3 << 6); // 清除睡眠位(SLEEP1和SLEEP12都设为0)
AD9834_WriteReg(control);
// 默认选择频率寄存器0和相位寄存器0
AD9834_SelectOutput(0, 0);
// 默认输出正弦波
AD9834_SetWaveform(0);
}
// 注意:以上代码仅供参考,请根据实际硬件连接和AD9834数据手册进行调整。
// 在main函数中初始化:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
AD9834_Init();
// 设置频率为1kHz
AD9834_SetFrequency(1000, 0); // 设置到FREQ0寄存器
while (1) {
}
}
/*
注意事项:
1. 请根据实际使用的MCLK频率修改f_mclk的值。
2. 如果使用软件方式选择频率寄存器(通过控制寄存器而不是PS引脚),则需要修改AD9834_SelectOutput函数,通过SPI发送控制字来设置FSELECT位(控制寄存器位10)和PSELECT位(控制寄存器位11)。
3. 以上代码没有包含相位设置,如有需要可以调用AD9834_SetPhase函数。
4. 在写入频率或相位寄存器后,AD9834可能需要一段时间来更新输出,但通常很快。
参考文献:
[1] AD9834数据手册,Analog Devices.
*/
§§相关问题§§
1. 如何调整AD9834的输出频率精度?
2. AD9834的SPI通信速率最高是多少?
3. 如何通过AD9834产生方波信号?</think>### AD9834驱动代码实现(基于STM32F103ZET6 HAL库与CubeMX)
#### 硬件连接
| AD9834引脚 | STM32引脚 | 功能说明 |
|------------|-----------|-------------------|
| PS | 自定义GPIO | 频率寄存器选择 |
| FS/FSYNC | SPI_NSS | 片选信号 |
| SCK | SPI_SCK | 时钟信号 |
| SDA | SPI_MOSI | 数据输入 |
| RST | 自定义GPIO | 复位信号 |
---
### 步骤1:CubeMX配置
1. **SPI配置**
- 模式:全双工主模式
- 数据大小:16位
- 时钟极性(CPOL):High
- 时钟相位(CPHA):2 Edge(模式3)
- 波特率:≤ 25MHz(参考AD9834手册)
- NSS信号:硬件模式(Hardware NSS Output Signal)
2. **GPIO配置**
- PS/RST引脚:设为GPIO输出模式(推挽输出,上拉)
- 其他SPI引脚自动配置
3. **时钟配置**
- 系统时钟:72MHz
- SPI时钟:18MHz(4分频)
---
### 步骤2:驱动代码实现
```c
/* 引脚定义 */
#define AD9834_PS_PIN GPIO_PIN_0
#define AD9834_PS_PORT GPIOA
#define AD9834_RST_PIN GPIO_PIN_1
#define AD9834_RST_PORT GPIOA
/* 寄存器地址 */
#define AD9834_REG_CTRL 0x2000
#define AD9834_REG_FREQ0 0x4000
#define AD9834_REG_FREQ1 0x8000
#define AD9834_REG_PHASE0 0xC000
#define AD9834_REG_PHASE1 0xE000
/* 波形类型 */
typedef enum {
AD9834_SINE = 0,
AD9834_TRIANGLE,
AD9834_SQUARE
} WaveformType;
/* 初始化函数 */
void AD9834_Init(SPI_HandleTypeDef *hspi) {
// 硬件复位
HAL_GPIO_WritePin(AD9834_RST_PORT, AD9834_RST_PIN, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(AD9834_RST_PORT, AD9834_RST_PIN, GPIO_PIN_SET);
// 默认选择频率寄存器0
HAL_GPIO_WritePin(AD9834_PS_PORT, AD9834_PS_PIN, GPIO_PIN_RESET);
// 发送复位命令(启用内部时钟,禁用睡眠模式)
uint16_t ctrl_cmd = AD9834_REG_CTRL | 0x2000; // RESET=1
AD9834_Write(hspi, ctrl_cmd);
// 设置默认波形(正弦波)
AD9834_SetWaveform(hspi, AD9834_SINE);
}
/* SPI写入函数 */
void AD9834_Write(SPI_HandleTypeDef *hspi, uint16_t data) {
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET); // 拉低FSYNC
HAL_SPI_Transmit(hspi, (uint8_t*)&data, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET); // 拉高FSYNC
}
/* 设置频率 */
void AD9834_SetFrequency(SPI_HandleTypeDef *hspi, uint8_t reg_sel, float freq, uint32_t mclk) {
// 计算频率字(28位)
uint32_t freq_word = (uint32_t)((freq * 268435456.0) / mclk); // 2^28 = 268435456
// 分高低14位写入
uint16_t low14 = (freq_word & 0x3FFF) | (reg_sel ? AD9834_REG_FREQ1 : AD9834_REG_FREQ0);
uint16_t high14 = ((freq_word >> 14) & 0x3FFF) | (reg_sel ? AD9834_REG_FREQ1 : AD9834_REG_FREQ0) | 0x4000;
AD9834_Write(hspi, low14);
AD9834_Write(hspi, high14);
// 更新PS引脚状态
HAL_GPIO_WritePin(AD9834_PS_PORT, AD9834_PS_PIN, reg_sel ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/* 设置波形 */
void AD9834_SetWaveform(SPI_HandleTypeDef *hspi, WaveformType type) {
uint16_t ctrl_cmd = AD9834_REG_CTRL;
switch(type) {
case AD9834_SINE: // MODE=0, OPBITEN=0
ctrl_cmd |= 0x0000;
break;
case AD9834_TRIANGLE: // MODE=1, OPBITEN=0
ctrl_cmd |= 0x0020;
break;
case AD9834_SQUARE: // MODE=0, OPBITEN=1, DIV2=0
ctrl_cmd |= 0x000A;
break;
}
AD9834_Write(hspi, ctrl_cmd);
}
```
---
### 步骤3:主函数调用示例
```c
int main(void) {
// HAL初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
// AD9834初始化
AD9834_Init(&hspi1);
// 设置1kHz正弦波(MCLK=25MHz)
AD9834_SetFrequency(&hspi1, 0, 1000.0, 25000000);
while (1) {
// 主循环
}
}
```
---
### 关键注意事项
1. **SPI模式**:必须使用模式3(CPOL=1, CPHA=1),符合AD9834在SCLK下降沿采样的时序要求[^1]。
2. **频率计算**:频率字计算公式为 $f_{out} = \frac{f_{reg} \times f_{MCLK}}{2^{28}}$,其中 $f_{reg}$ 为28位频率字。
3. **复位时序**:RST引脚低电平保持至少10ns(建议1ms以上)。
4. **寄存器选择**:PS引脚电平决定当前使用的频率寄存器(0=FREQ0, 1=FREQ1)。
[^1]: AD9834数据手册,Analog Devices Inc.
---
阅读全文
相关推荐


















