【STM32H7S78-DK】Evaluation + SPI application driving AD9833
[Copy link]
Since the STM32H7S78 does not have a DAC peripheral, there are usually two ways to generate analog signals. One is PWM plus output filter, and the other is a dedicated DDS chip.
The AD9833 is a low power, programmable waveform generator capable of generating sine, triangle, and square wave outputs. Waveform generators are required for various types of detection, signal excitation, and time domain reflection (TDR) applications. The output frequency and phase are software programmable and easy to adjust. No external components are required. The frequency register is 28 bits wide, and a resolution of 0.1 Hz can be achieved at a clock rate of 25 MHz; at a clock rate of 1 MHz, the AD9833 can achieve a resolution of 0.004 Hz.
The AD9833 is written to via a 3-wire serial interface. The serial interface can operate at clock rates up to 40 MHz and is compatible with DSP and microcontroller standards. The device operates from a 2.3 V to 5.5 V supply. Home Features:
characteristic
- Digitally programmable frequency and phase
- When the supply voltage is 3 V, the power consumption is 12.65 mW
- Output frequency range: 0 MHz to 12.5 MHz
- 28-bit resolution (0.1 Hz at 25 MHz reference clock)
- Sine wave/triangle wave/square wave output
- 2.3 V to 5.5 V power supply
- No external components required
- Three-wire SPI interface
- Extended temperature range: -40°C to +105°C
- Power-down option
- 10-lead MSOP package
- Qualified for automotive applications
AD9833-EP supports defense and aerospace applications (AQEC standard)
- Download AD9833-EP data sheet (pdf)
- Temperature range: 55°C to +125°C
- Controlled manufacturing baseline
- An assembly/test plant
- A manufacturing plant
- Enhanced Product Change Notification
- Certification data available on request
- V62/14619-01XE DSCC drawing number
You can use the SPI interface to communicate with AD9833. Usually you can use GPIO simulation or hardware SPI interface. This post uses the hardware SPI interface. Looking at the schematic diagram, you can see that the SPI interface on the Arduino interface is SPI4, and the chip select uses PF8, not the hardware chip select signal:
The SPI interface is configured as follows:
PF8 is configured as GPIO as chip select:
Then define the relevant registers in the AD98333.h header file:
#ifndef INC_AD9833_H_
#define INC_AD9833_H_
#include "main.h"
/*** Redefine if necessary ***/
#define AD9833_SPI_PORT hspi4
extern SPI_HandleTypeDef AD9833_SPI_PORT;
/*** Control Register Bits (DataSheet AD9833 p. 14, Table 6) ***/
#define B28_CFG (1 << 13)
#define HLB_CFG (1 << 12)
#define F_SELECT_CFG (1 << 11)
#define P_SELECT_CFG (1 << 10)
#define RESET_CFG (1 << 8)
#define SLEEP1_CFG (1 << 7)
#define SLEEP12_CFG (1 << 6)
#define OPBITEN_CFG (1 << 5)
#define DIV2_CFG (1 << 3)
#define MODE_CFG (1 << 1)
/*** Bitmask to register access ***/
#define FREQ0_REG 0x4000
#define PHASE0_REG 0xC000
//#define FREQ1_ACCESS 0x8000
//#define PHASE1_ACCESS 0xE000
/*** Waveform Types (DataSheet p. 16, Table 15) ***/
#define WAVEFORM_SINE 0
#define WAVEFORM_TRIANGLE MODE_CFG
#define WAVEFORM_SQUARE OPBITEN_CFG | DIV2_CFG
#define WAVEFORM_SQUARE_DIV2 OPBITEN_CFG
/*** Sleep Modes ***/
#define NO_POWERDOWN 0
#define DAC_POWERDOWN SLEEP12_CFG
#define CLOCK_POWERDOWN SLEEP1_CFG
#define FULL_POWERDOWN SLEEP12_CFG | SLEEP1_CFG
#define FMCLK 25000000
#define BITS_PER_DEG 11.3777777777778 // 4096 / 360
typedef enum {
wave_triangle,
wave_square,
wave_sine,
} WaveDef;
/*
* [url=home.php?mod=space&uid=159083]@brief[/url] Set signal generation frequency
* @param Frequency value in uint32_t format
*/
void AD9833_SetFrequency(uint32_t freq);
/*
* @brief Set signal generation waveform
* @param Waveform in WaveDef Type declared in .h file
*/
void AD9833_SetWaveform(WaveDef Wave);
/*
* @brief Set signal generation phase
* @param Phase in degrees in uint16_t format. Value can be large then 360
*/
void AD9833_SetPhase(uint16_t phase_deg);
/*
* @brief AD9833 Initial Configuration
* @param Type of Waveform, Frequency, Phase in degrees
*/
void AD9833_Init(WaveDef Wave, uint32_t freq, uint16_t phase_deg);
/*
* @brief Enable or disable the output of the AD9833
* @param Output state (ON/OFF)
*/
void AD9833_OutputEnable(uint8_t output_state);
/*
* @brief Set Sleep Mode Function (Explained in datasheet Table 14)
* @param Mode of sleep function defined in title
*/
void AD9833_SleepMode(uint8_t mode);
#endif /* INC_AD9833_H_ */
Source code AD9833.c implements initialization, frequency, and phase settings:
//Setup Hardware SPI to POLATRITY HIGH, PHASE 1 EDGE
#include "AD9833.h"
uint8_t _waveform = WAVEFORM_SINE;
uint8_t _sleep_mode = NO_POWERDOWN;
uint8_t _freq_source = 0;
uint8_t _phase_source = 0;
uint8_t _reset_state = 0;
/*
* @brief Set Chip Select pin to LOW state
*/
static void AD9833_Select(void)
{
HAL_GPIO_WritePin(AD9833_FSYNC_GPIO_Port, AD9833_FSYNC_Pin, GPIO_PIN_RESET);
}
/*
* @brief Set Chip Select pin to HIGH state
*/
static void AD9833_Unselect(void)
{
HAL_GPIO_WritePin(AD9833_FSYNC_GPIO_Port, AD9833_FSYNC_Pin, GPIO_PIN_SET);
}
/*
* @brief Send data by SPI protocol
* @param Data variable in uint16_t format
*/
static void AD9833_WriteRegister(uint16_t data)
{
AD9833_Select();
uint8_t LByte = data & 0xff;
uint8_t HByte = (data >> 8) & 0xff;
HAL_SPI_Transmit(&AD9833_SPI_PORT, &HByte, 1, HAL_MAX_DELAY);
HAL_SPI_Transmit(&AD9833_SPI_PORT, &LByte, 1, HAL_MAX_DELAY);
AD9833_Unselect();
}
/*
* @brief Update Control Register Bits
*/
static void AD9833_WriteCfgReg(void)
{
uint16_t cfg = 0;
cfg |= _waveform;
cfg |= _sleep_mode;
cfg |= (_freq_source ? F_SELECT_CFG : 0); //it's unimportant because don't use FREQ1
cfg |= (_phase_source ? P_SELECT_CFG : 0); //it's unimportant because don't use PHASE1
cfg |= (_reset_state ? RESET_CFG : 0);
cfg |= B28_CFG;
AD9833_WriteRegister(cfg);
}
void AD9833_SetWaveform(WaveDef Wave)
{
if (Wave == wave_sine) _waveform = WAVEFORM_SINE;
else if (Wave == wave_square) _waveform = WAVEFORM_SQUARE;
else if (Wave == wave_triangle) _waveform = WAVEFORM_TRIANGLE;
AD9833_WriteCfgReg();
}
void AD9833_SetFrequency(uint32_t freq)
{
// TODO: calculate max frequency based on refFrequency.
// Use the calculations for sanity checks on numbers.
// Sanity check on frequency: Square - refFrequency / 2
// Sine/Triangle - refFrequency / 4
if (freq > (FMCLK >> 1)) //bitwise FMCLK / 2
freq = FMCLK >> 1;
else if (freq < 0) freq = 0;
uint32_t freq_reg = (float)freq * (float)((1 << 28) / FMCLK); // Tuning word
uint16_t LSB = FREQ0_REG | (freq_reg & 0x3FFF);
uint16_t MSB = FREQ0_REG | (freq_reg >> 14);
AD9833_WriteCfgReg(); // Update Config Register
AD9833_WriteRegister(LSB);
AD9833_WriteRegister(MSB);
}
void AD9833_SetPhase(uint16_t phase_deg)
{
if(phase_deg < 0) phase_deg = 0;
else if (phase_deg > 360) phase_deg %= 360;
uint16_t phase_val = ((uint16_t)(phase_deg * BITS_PER_DEG)) & 0xFFF;
AD9833_WriteRegister(PHASE0_REG | phase_val);
}
void AD9833_Init(WaveDef Wave, uint32_t freq, uint16_t phase_deg)
{
AD9833_OutputEnable(0);
AD9833_SetWaveform(Wave);
AD9833_WriteCfgReg();
AD9833_SetFrequency(freq);
AD9833_SetPhase(phase_deg);
AD9833_OutputEnable(1);
}
void AD9833_SleepMode(uint8_t mode)
{
_sleep_mode = mode;
AD9833_WriteCfgReg();
}
void AD9833_OutputEnable(uint8_t output_state)
{
_reset_state = !output_state;
AD9833_WriteCfgReg();
}
Finally, call the interface initialization:
AD9833_Init(wave_sine, 100, 0);
The output frequency is 100Hz sine wave.
wiring:
AD9833 module:
The waveform effect is as follows, which shows that the chip is driven correctly:
|